Query in postgresql - postgresql

I have two tables t1 and t2 as following
t1
A B C D E
1 2 c d e
3 1 d e f
4 2 f g h
t2
A B
1 2
8 6
4 2
Here A,B,C,D,E are the columns of t1 and A,B are the columns of t2 where A and B are common columns.
What I have done so far
I have written the following query
WITH temp as (
select *
from t2
)
select tab1.*
from t1 tab1, temp tab2
where (tab1.A!=tab2.A OR tab1.B!=tab2.B)
I wanted this output
A B C D E
3 1 d e f
But I am getting this output
A B C D E
1 2 c d e
1 2 c d e
3 1 d e f
3 1 d e f
3 1 d e f
4 2 f g h
4 2 f g h
What query should I use?

If I understand you correctly, you'd like those rows from T1 that don't have corresponding rows in T2. The easiest way in my opinion is a LEFT OUTER JOIN:
psql=> select * from t1;
a | b | c | d | e
---+---+---+---+---
1 | 2 | c | d | e
3 | 1 | d | e | f
4 | 2 | f | g | h
(3 rows)
psql=> select * from t2;
a | b
---+---
1 | 2
8 | 6
4 | 2
(3 rows)
psql=> select t1.a, t1.b, t1.c, t1.d, t1.e from t1 left outer join t2 on (t1.a = t2.a and t1.b = t2.b) where t2.a is null;
a | b | c | d | e
---+---+---+---+---
3 | 1 | d | e | f
(1 row)
Edit: Here's the select without the where clause, with the rows from t2 added (otherwise it'd be just like a select * from t1). As you can see, the first row contains NULLs for t2_a and t2_b:
psql=> select t1.a, t1.b, t1.c, t1.d, t1.e, t2.a as t2_a, t2.b as t2_b from t1 left outer join t2 on (t1.a = t2.a and t1.b = t2.b);
a | b | c | d | e | t2_a | t2_b
---+---+---+---+---+------+------
3 | 1 | d | e | f | |
1 | 2 | c | d | e | 1 | 2
4 | 2 | f | g | h | 4 | 2
(3 rows)

How about:
SELECT * FROM t1 WHERE (a,b) NOT IN (SELECT a,b FROM t2);

Related

How to construct a LATERAL JOIN in SQLAlchemy

I have a table tb like this:
db=# SELECT * FROM tb ORDER by id;
id | name | luckynumber
----+--------+-------------
1 | Alice | 100
2 | Bob | 300
3 | Chad | 13
4 | Dave | 123
5 | Eve | 130
6 | Faythe | 777
(6 rows)
An SQL below returns modulo of each person`s luckynumber to Chad's lucky number:
db=# SELECT name, mod(a.luckynumber, b.luckynumber) as modulo FROM tb AS a,
LATERAL (SELECT id, luckynumber FROM tb WHERE name = 'Chad') AS b
WHERE a.id <> b.id ORDER by modulo;
name | modulo
--------+--------
Eve | 0
Bob | 1
Dave | 6
Alice | 9
Faythe | 10
(5 rows)
What is a SQLAlchemy equivalent of this SQL ? Thank you!
In complement as a_horse_with_no_name... Or with a simple join...
select a.name, mod(a.luckynumber, b.luckynumber) as modulo
FROM tb AS a
join tb as b ON a.id <> b.id
where b.name = 'Chad'
ORDER by modulo;

postgresql find similar word groups

I have a table1 containing a column A, where ~100,000 strings (varchar) are stored. Unfortunately, each string has multiple words which are seperated with spaces. Further they have different length, i.e. one string can consist of 3 words while an other string contains 7 words.
Then I have a column B stored in a second table2 which contains only 100 strings in the same manner. Hence, multiple words per string, seperated by spaces.
The target is, to look how likely a record of Column B is matching with probably multiple records of column A based on the words. The result should also have a ranking. I was thinking of using full text search in a loop but I don't know how to do this, or if there is a proper way to achieve this?
I don't know if you can "tturn" table to a dictionary to use full text for ranking here. But you can query it with some primityve ranking quite easily, eg:
t=# with a(a) as (values('a b c'),('a c d'),('b e f'),('r b t'),('q w'))
, b(i,b) as (values(1,'a b'), (2,'e'), (3,'b'))
, p as (select unnest(string_to_array(b.b,' ')) arr,i from b)
select a phrases,arr match_words,count(1) over (partition by arr) words_in_matches, count(1) over (partition by i) matches,i from a left join p on a.a like '%'||arr||'%';
phrases | match_words | words_in_matches | matches | i
---------+-------------+------------------+---------+---
r b t | b | 6 | 5 | 1
a b c | b | 6 | 5 | 1
b e f | b | 6 | 5 | 1
a b c | a | 2 | 5 | 1
a c d | a | 2 | 5 | 1
b e f | e | 1 | 1 | 2
r b t | b | 6 | 3 | 3
a b c | b | 6 | 3 | 3
b e f | b | 6 | 3 | 3
q w | | 1 | 1 |
(10 rows)
phrases are rows from your big table.
match_words are tokens from your small table (splitted by spaces)
words_in_matches amount of tokens in phrases
matches is amount of matches in big table phrases from small table phrases
i index of phrase from small table
So you can order by third or fourth column to get some sort of ranking...

Unions of intersecting sets

I have a table representing sets of records like
set_id | record_id
a | 1
a | 2
a | 3
b | 2
b | 4
b | 5
c | 6
c | 7
d | 9
d | 11
e | 10
f | 11
f | 12
I want to yield output like this
output
{1, 2, 3, 4, 5}
{6, 7}
{9, 11, 12}
{10}
Where intersecting sets are combined (notice set a and set b have been combined; d and f have also been combined).
Is there a good way of doing this with SQL, not a stored procedure. I know that I'm looking for a kind of Union-Find procedure.
prepare:
so=> create table so75(set_id text, record_id int);
CREATE TABLE
so=> copy so75 from stdin;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> ^CERROR: COPY from stdin failed: canceled by user
CONTEXT: COPY so75, line 1
so=> copy so75 from stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> a | 1
a | 2
a | 3
b | 2
b | 4
b | 5
c | 6
c | 7
d | 9
d | 11
e | 10
f | 11
f | 12
>> >> >> >> >> >> >>
>> \.
COPY 14
qry:
so=> with keys as (
with a as (
select *,count(1) over (partition by record_id) c, array_agg(set_id) over(partition by record_id) cc
from so75
)
select set_id, cc
from a where c > 1
)
select distinct array_agg(distinct record_id)
from so75
left outer join keys on keys.set_id = so75.set_id
group by case when array_length(cc,1) > 1 then cc::text else so75.set_id end;
array_agg
-------------
{6,7}
{10}
{1,2,3,4,5}
{9,11,12}
(4 rows)

How to produce grouped column from many rows on PostgreSQL

If i could produce this result from many to many relationship from this kind of query:
SELECT x1.id AS id1, x3.id AS id3
FROM humans x1
LEFT JOIN memberships x2
ON x1.id = x2.human_id
LEFT JOIN groups x3
ON x2.group_id = x3.id
WHERE x1.id IN ( 1,2,3,4 )
ORDER BY 1,2
id1 | id3
----+----
1 | A
1 | B
1 | C
2 | D
2 | E
3 | F
4 | (null)
5 | G
5 | Z
how to convert it into this kind of table?
id1 | id3s
----+--------
1 | A, B, C
2 | D, E
3 | F
4 | (null)
5 | G, Z
Use string_agg and a group by:
SELECT x1.id AS id1, string_agg(x3.id,',' order by x3.id asc) AS id3s
FROM humans x1
LEFT JOIN memberships x2
ON x1.id = x2.human_id
LEFT JOIN groups x3
ON x2.group_id = x3.id
WHERE x1.id IN ( 1,2,3,4 )
GROUP BY x1.id
ORDER BY 1

postgres counting one record twice if it meets certain criteria

I thought that the query below would naturally do what I explain, but apparently not...
My table looks like this:
id | name | g | partner | g2
1 | John | M | Sam | M
2 | Devon | M | Mike | M
3 | Kurt | M | Susan | F
4 | Stacy | F | Bob | M
5 | Rosa | F | Rita | F
I'm trying to get the id where either the g or g2 value equals 'M'... But, a record where both the g and g2 values are 'M' should return two lines, not 1.
So, in the above sample data, I'm trying to return:
$q = pg_query("SELECT id FROM mytable WHERE ( g = 'M' OR g2 = 'M' )");
1
1
2
2
3
4
But, it always returns:
1
2
3
4
Your query doesn't work because each row is returned only once whether it matches one or both of the conditions. To get what you want use two queries and use UNION ALL to combine the results:
SELECT id FROM mytable WHERE g = 'M'
UNION ALL
SELECT id FROM mytable WHERE g2 = 'M'
ORDER BY id
Result:
1
1
2
2
3
4
you might try a UNION along these lines:
"SELECT id FROM mytable WHERE ( g = 'M') UNION SELECT id FROM mytable WHERE ( g2 = 'M')"
Hope this helps, Martin
SELECT id FROM mytable WHERE g = 'M'
UNION
SELECT id FROM mytable WHERE g2 = 'M'