Operator equivalent to 'includes' - postgresql

I'm trying to JOIN a bunch of tables and I'm new to Postgres and Knex.
Is there an equivalent to the javascript operator 'includes'? e.g. I want to do something like this:
JOIN tbl ON somearrayofIds 'includes' tbl.id
Here's my fetch query:
db('posts AS p')
.select(
'p.id as postId',
'p.user_id as pUserId',
'p.content',
'p.created_at',
'p.updated_at',
'p.image_url as postImage',
'pr.post_id as prPostId',
'pr.reaction_ids as prReactionIds',
'pr.user_ids as prUserIds',
'r.id as rId',
'r.keyword as rKeyword',
)
.leftJoin('post_reactions AS pr', 'pr.post_id', '=', 'p.id')
.leftJoin('reactions AS r', 'r.id', 'includes', 'pr.reaction_ids') // r.id is string, pr.reaction_ids is array

Something like this?:
create table array_test(id int, array_fld int[]);
insert into array_test values (1, '{1, 3, 5}');
select * from array_test where 1 = ANY(array_fld);
id | array_fld
----+-----------
1 | {1,3,5}
select * from array_test where 2 = ANY(array_fld);
id | array_fld
----+-----------
(0 rows)

Related

Combine IN and LIKE function in DB2

Is there a way to combine IN and LIKE function together in DB2? For example I would like to exclude users that have userid A,B,C and also userid that start from X% or Y% . I tried the below query however it did not work
select * from table where userid not in ('A','B','C') or (not like 'X%' or not like 'Y%')
Use 'AND' instead of 'OR'
select * from table
where userid not in ('A','B','C')
and userid not like 'X%'
and userid not like 'Y%'
You may use all the constants used in IN in LIKE:
with
table (userid) as
(
values 'A', 'AA', 'XX', 'YY', 'ZZ'
)
, vals (userid) as
(
values 'A', 'B', 'C', 'X%', 'Y%'
)
select *
from table t
where not exists
(
select 1
from vals v
where t.userid like v.userid
);
The result is:
|USERID|
|------|
|AA |
|ZZ |

postgresql how to draft not in statement with join table

I have 3 postgresql tables : Documents, Keywords and a join table.
I have query that searches document.id and document.date if certain keywords are related to that document. That works fine like so:
SELECT
documents.id, documents.document_date
FROM
documents
INNER JOIN
documents_keywords ON documents_keywords.document_id = documents.id
INNER JOIN
keywords ON keywords.id = documents_keywords.keyword_id
WHERE
keywords.keyword IN ('bread' , 'cake')
GROUP BY documents.id
This returns:
id | document_date
----+-----------
4 | 1200
12 | 1280
(2 rows)
I also want to exclude keywords. I thought I could do NOT IN like so:
SELECT
documents.id, documents.document_date
FROM
documents
INNER JOIN
documents_keywords ON documents_keywords.document_id = documents.id
INNER JOIN
keywords ON keywords.id = documents_keywords.keyword_id
WHERE
keywords.keyword NOT IN ('cranberries')
GROUP BY documents.id
But that always returns empty, whatever keyword I put:
id | document_date
----+-----------
(0 rows)
This is incorrect. I expected:
id | document_date
----+-----------
4 | 1200
(1 row)
You might want to use an array expression, like this:
WHERE keyword = any(array['bread', 'cake'])
when you want to include a row.
If you want to exclude something, you have to do the NOT IN over a subselect of the inverse condition, e.g.
SELECT ... WHERE document_id NOT IN
(SELECT document_id FROM ...joins... WHERE keyword = ANY(array['cranberry']))
Here is an example I put together:
WITH documents(d_id, date) AS (
VALUES(1,'1000'),(2,'2000'),(3,'3000'),(4,'4000')
),
keywords(k_id, keyword) AS (
VALUES(1, 'cake'), (2, 'bread'), (3, 'cranberry')
),
documents_keywords (d_id, k_id) AS (
VALUES(1,1),(1,2),(2,2),(2,3),(3,3)
)
SELECT * FROM documents where d_id NOT IN (
SELECT d_id FROM
documents
JOIN documents_keywords USING(d_id)
JOIN keywords USING(k_id)
WHERE keyword = ANY(array['cranberry'])
)
Also, I am not sure why you are using GROUP BY, I don't think you need it.

How to count the frequency of integers in a set of querystrings in postgres

I have a column in a postgres database which logs search querystrings for a page on our website.
The column contains data like
"a=2&b=4"
"a=2,3"
"b=4&a=3"
"a=4&a=3"
I'd like to work out the frequency of each value for a certain parameter (a).
value | freq
------|------
3 | 3
2 | 2
4 | 1
Anyway to do this in a single SQL statement?
Something like this:
with all_values as (
select string_to_array(split_part(parameter, '=', 2), ',') as query_params
from the_table d,
unnest(string_to_array(d.querystring, '&')) as x(parameter)
where x.parameter like 'a%'
)
select t.value, count(*)
from all_values av, unnest(av.query_params) as t(value)
group by t.value
order by t.value;
Online example: http://rextester.com/OXM67442
try something like this :
select data_value,count(*) from (
select data_name,unnest(string_to_array(data_values,',')) data_value from (
select split_part(data_array,'=',1) data_name ,split_part(data_array,'=',2) data_values from (
select unnest(string_to_array(mydata,'&')) data_array from mytable
) a
) b
) c where data_name='a' group by 1 order by 1
Assuming tha table that keeps the counts is called paramcount:
WITH vals(v) AS
(SELECT regexp_replace(p, '^.*=', '')
FROM regexp_split_to_table(
'b=4&a=3,2',
'&|,'
) p(p)
)
INSERT INTO paramcount (value, freq)
SELECT v, 1 FROM vals
ON CONFLICT (value)
DO UPDATE SET freq = paramcount.freq + 1
WHERE paramcount.value = EXCLUDED.value;
get csv integer after 'a='
split that to numbers
stat values
select v, count(*) from (
SELECT c,unnest(string_to_array(unnest(regexp_matches(c,'a=([0-9,]+)','g')),',')) as v FROM qrs
) x group by v;
Parametrize:
WITH argname(aname) as (values ('a'::TEXT))
select v, count(*) from (SELECT c,unnest(string_to_array(unnest(regexp_matches(c,aname||'=([0-9,]+)','g')),',')) as v FROM qrs,argname) x group by v;

SQL to remove rows with duplicated value while keeping one

Say I have this table
id | data | value
-----------------
1 | a | A
2 | a | A
3 | a | A
4 | a | B
5 | b | C
6 | c | A
7 | c | C
8 | c | C
I want to remove those rows with duplicated value for each data while keeping the one with the min id, e.g. the result will be
id | data | value
-----------------
1 | a | A
4 | a | B
5 | b | C
6 | c | A
7 | c | C
I know a way to do it is to do a union like:
SELECT 1 [id], 'a' [data], 'A' [value] INTO #test UNION SELECT 2, 'a', 'A'
UNION SELECT 3, 'a', 'A' UNION SELECT 4, 'a', 'B'
UNION SELECT 5, 'b', 'C' UNION SELECT 6, 'c', 'A'
UNION SELECT 7, 'c', 'C' UNION SELECT 8, 'c', 'C'
SELECT * FROM #test WHERE id NOT IN (
SELECT MIN(id) FROM #test
GROUP BY [data], [value]
HAVING COUNT(1) > 1
UNION
SELECT MIN(id) FROM #test
GROUP BY [data], [value]
HAVING COUNT(1) <= 1
)
but this solution has to repeat the same group by twice (consider the real case is a massive group by with > 20 columns)
I would prefer a simpler answer with less code as oppose to complex ones. Is there any more concise way to code this?
Thank you
You can use one of the methods below:
Using WITH CTE:
WITH CTE AS
(SELECT *,RN=ROW_NUMBER() OVER(PARTITION BY data,value ORDER BY id)
FROM TableName)
DELETE FROM CTE WHERE RN>1
Explanation:
This query will select the contents of the table along with a row number RN. And then delete the records with RN >1 (which would be the duplicates).
This Fiddle shows the records which are going to be deleted using this method.
Using NOT IN:
DELETE FROM TableName
WHERE id NOT IN
(SELECT MIN(id) as id
FROM TableName
GROUP BY data,value)
Explanation:
With the given example, inner query will return ids (1,6,4,5,7). The outer query will delete records from table whose id NOT IN (1,6,4,5,7).
This fiddle shows the records which are going to be deleted using this method.
Suggestion: Use the first method since it is faster than the latter. Also, it manages to keep only one record if id field is also duplicated for the same data and value.
I want to add MYSQL solution for this query
Suggestion 1 : MySQL prior to version 8.0 doesn't support the WITH clause
Suggestion 2 : throw this error (you can't specify table TableName for update in FROM clause
So the solution will be
DELETE FROM TableName WHERE id NOT IN
(SELECT MIN(id) as id
FROM (select * from TableName) as t1
GROUP BY data,value) as t2;

mySQL query: How to insert with UNION?

I am kind of new to mySQL:s union functions, at least when doing inserts with them. I have gotten the following to work based upon a example found on the net:
INSERT INTO tableOne(a, b)
SELECT a, $var FROM tableOne
WHERE b = $var2
UNION ALL SELECT $var,$var
Ok, nothing strange about that. But what happens when I want to insert a third value into the database that has nothing to do with the logic of the Select being done?
Like : INSERT INTO tableOne(a, b, c )
How could that be done?
You can "select" literal values too:
mysql> select 'hello', 1;
+-------+---+
| hello | 1 |
+-------+---+
| hello | 1 |
+-------+---+
1 row in set (0.00 sec)
Hence, you can also use that in INSERT INTO ... SELECT FROM and UNIONs.
INSERT INTO someTable (a, b, c) VALUES
SELECT id, name, 5
FROM someOtherTable
UNION
SELECT id, alias, 8
FROM anotherTable