join two SQL rows in a single one - sql

I have three tables in Postgresql, for a biological classification system.
table lang (languages)
id name
1 português
2 english
-------------------------------
table taxon (biological groups)
id name
...
101 Mammalia
-------------------------------
table pop (popular names)
id tax lang pop
...
94 101 1 mamíferos
95 101 2 mammals
I want to get
id name namePT nameEN
101 Mammalia mamíferos mammals
but my join is giving me
id name pop
101 Mammalia mamíferos
101 Mammalia mammals
select t.id,name,pop from taxon t
left join pop p on p.tax = t.id
where t.id = 101
How can I get the desired result in a single row?

If you are happy to change query every time you add a new language then this query will do the trick:
select t.id,name,pe.pop as eng_pop, pp.pop as port_pop
from taxon t
left join pop pe on pe.tax = t.id and pe.lang = 1
left join pop pp on pp.tax = t.id and pp.lang = 2
where t.id = 101

You could use this
SELECT t.id, t.name,
MAX(CASE WHEN p.lang = 1 THEN p.pop END) AS namePT,
MAX(CASE WHEN p.lang = 2 THEN p.pop END) AS nameEN
FROM taxon t
LEFT JOIN pop p
ON p.tax = t.id
GROUP BY t.id, t.name;

Here's how I got the results:
with base as (
select t.id, t.name,
case when lang = 1 then 'mamiferos' else null end as namePT,
case when lang = 2 then 'mamals' else null end as nameEN
from taxon t
left join pop p on t.id = p.tax
group by 1,2,3, p.lang
)
select
distinct id,
name,
coalesce(namept,'mamiferos',null) as namept,
coalesce(nameen,'mamals',null) as nameen
from base
where id = 101
group by id, name, namept, nameen;
id | name | namept | nameen
-----+----------+-----------+--------
101 | Mammalia | mamiferos | mamals
(1 row)

Related

SQL - must return 0 if GROUP_CONCAT value is null

I just want to not return any row if no result!
I'm so null in sql that it depresses me, please help..
I have 2 tables with 1 association table :
shop table:
id
name
adress
1
name1
1 street..
2
name2
2 street..
3
name3
3 street..
activity table:
id
title
1
Distribution
2
Importation
3
Préparation
4
Production
shop_activity association table:
shop_id
activity_id
1
1
2
2
3
3
3
4
I found this request to concat activities:
SELECT s.*, GROUP_CONCAT(a.title SEPARATOR ',') AS activities
FROM shop s
LEFT JOIN shop_activity sa
ON s.id = sa.shop_id
LEFT JOIN activity a
ON sa.activity_id = a.id
WHERE s.id = ?
and it does the job
id
name
adress
activities
3
name3
3 street..
Production, Importation
but if s.id doesn't exist in table shop, currently I have this result:
id
name
adress
activities
NULL
NULL
NULL
NULL
but I don't want any row if all is null to return a message, currently I make a condition on id column and I know that it isn't very clean!
Try just join instead of LEFT:
SELECT s.*, GROUP_CONCAT(a.title SEPARATOR ',') AS activities
FROM shop s
JOIN shop_activity sa
ON s.id = sa.shop_id
JOIN activity a
ON sa.activity_id = a.id
WHERE s.id = ?
I've found: just group by
SELECT s.*, GROUP_CONCAT(a.title SEPARATOR ',') AS activities
FROM shop s
LEFT JOIN shop_activity sa ON s.id = sa.shop_id
LEFT JOIN activity a ON sa.activity_id = a.id
WHERE s.id = ?
GROUP By s.id

SQL query to get info from different tables

Need a help with sql query.
I have 2 tables.
Matches.
first_referee_arbitr_id
second_referee_arbitr_id
home_team
guest_team
date
Arbitrs.
id
name
surname
first_referee_arbitr_id and second_referee_arbitr_id are from Arbitrs table(id).
As the result I need to count how much each referee has been included in matches:
referee_first_count | referee_second_count | Arbitr Surname | Arbitr Name
My query now:
SELECT T1.referee_first_count, T2.referee_second_count, T1.surname, T1.name
FROM (
select matches.first_referee_arbitr_id, q.surname, q.name, count(*) AS referee_first_count
FROM matches
JOIN (
SELECT name, surname, id
FROM arbitrs
) as q ON matches.first_referee_arbitr_id=q.id
GROUP BY matches.first_referee_arbitr_id, q.name, q.surname
ORDER BY referee_first_count DESC
) T1 FULL OUTER JOIN
(
select matches.second_referee_arbitr_id, arbitr.surname, arbitr.name, count(*) AS referee_second_count
FROM matches
JOIN (
SELECT name, surname, id
FROM arbitrs
) as arbitr ON matches.second_referee_arbitr_id=arbitr.id
GROUP BY matches.second_referee_arbitr_id, arbitr.name, arbitr.surname
ORDER BY referee_second_count DESC
) T2
ON (
(T1.name = T2.name) AND (T1.surname = T2.surname)
)
Example
Arbitrs:
id | Name | Surname
1 John Rambler
2 Steve Crystler
3 Tom Ferguson
Matches:
first_referee_arbitr_id| second_referee_arbitr_id| home_team | guest_team | date
1 Chelsea Everton 22.06.2020
2 1 Liverpool Lester 28.06.2020
3 Dinamo K Dinamo M 06.07.2020
1 2 Juventus Dinamo K 10.10.2020
3 Dinamo K Chelsea 20.20.2020
Result:
referee_first_count | referee_second_count | surname | name
2 1 Rambler John
1 1 Crystler Steve
2 0 Ferguson Tom
My result now with query above:
referee_first_count | referee_second_count | surname | name
2 1 Rambler John
1 1 Crystler Steve
2 NULL NULL NULL
First aggregate the matches table (twice) and left join this to the Arbitrs table:
SELECT a.name , a.Surname , ISNULL(fir.first_count,0) , ISNULL( sec.sec_count,0)
FROM Arbitrs a
LEFT JOIN (
SELECT first_referee_arbitr_id,COUNT(*) as first_count
FROM Matches
GROUP BY first_referee_arbitr_id
) fir
ON fir.first_referee_arbitr_id = a.id
LEFT JOIN (
SELECT second_referee_arbitr_id,COUNT(*) as sec_count
FROM Matches
GROUP BY second_referee_arbitr_id
) sec
ON sec.second_referee_arbitr_id = a.id
SELECT
a.name
, a.surname
, COUNT(CASE WHEN m1.id IS NOT NULL THEN 1 end) referee_first_count
, COUNT(CASE WHEN m2.id IS NOT NULL THEN 1 end) referee_second_count
FROM
Arbitrs a
LEFT JOIN macthes m1
ON a.id = m.first_referee_arbitr_id
LEFT JOIN macthes m2
ON a2.id = m.second_referee_arbitr_id
GROUP BY
a.name
, a.surname
-- if you want to show only those with refrence
HAVING referee_first_count > 0 or referee_second_count > 0

Find parent id when all children share the same value

I have some data that looks a little like this
Table C
id | end_time
-------------
1 '2019-01-01'
2 '2020-01-01'
3 '2019-07-01'
Table F
id | parent_id
12 | 1
13 | 1
21 | 2
22 | 2
31 | 3
32 | 3
33 | 3
34 | 3
Table oui
rel_id | Product Version
1 '2'
12 '2'
13 '1'
2 '1'
21 '2'
22 '1'
3 '2'
31 '1'
32 '1'
33 '1'
34 '1'
Data relationship:
c.id = f.parent_id
c.id or f.id = oui.rel_id
What I'm trying to find is where the rel_id for C in table oui is the parent's product version is 2, but ALL children are version 1.
I found a similar question over here: Find ID of parent where all children exactly match but couldn't quite adapt it to this use case.
Expected result:
c.id
----
3
Reasoning: Both c.id 1/2 have children which have at least 1 item in product version 2.
Try this below logic-
DEMO HERE
SELECT ID FROM C
WHERE ID NOT IN
(
SELECT C.ID
FROM C
INNER JOIN F ON C.id = F.parent_id
INNER JOIN oui ON F.ID = Oui.rel_id
WHERE C.ID = CAST(oui.Product_Version AS INT)
-- by default your column "Product Version" should be INT in table oui
)
The issue you mentioned in the below comment, you can try this opposite conversion as below-
SELECT ID FROM C
WHERE ID NOT IN
(
SELECT C.ID
FROM C
INNER JOIN F ON C.id = F.parent_id
INNER JOIN oui ON F.ID = Oui.rel_id
WHERE CAST(C.ID AS VARCHAR) = oui.Product_Version
)
You want to get C entries with product version = 2 for which exists F entries with product version = 1 and not exist F entries with product version <> 1.
I don't know why there is a separate table OUI at all. One would expect the product version to be a mere column in the tables C and F instead.
So, let's use two with clauses to get to better tables :-)
with better_c as (select c.*, oui.product_version from c join oui on oui.rel_id = c.id)
, better_f as (select f.*, oui.product_version from f join oui on oui.rel_id = f.id)
The real query can then be written with INTERSECT and EXECPT:
with ...
select id from better_c where product_version = 2
intersect
select parent_id from better_f where product_version = 1
except
select parent_id from better_f where product_version <> 1;
The same with [NOT] EXISTS:
with ...
select id
from better_c
where product_version = 2
and exists
(select null from better_f where product_version = 1 and parent_id = better_c.id)
and not exists
(select parent_id from better_f where product_version <> 1 and parent_id = better_c.id);
The same with [NOT] IN:
with ...
select id
from better_c
where product_version = 2
and id in (select parent_id from better_f where product_version = 1)
and id not in (select parent_id from better_f where product_version <> 1);
Try This:
select
t1.id
from c "t1"
inner join oui "t2" on t2.rel_id=t1.id
where t2.product_version='2' -- product_version for Parent
and
(select
count(*)
from f "t3"
inner join oui t4 on t4.rel_id=t3.id
where t4.product_version !='1' -- product_version for Child
and t3.parent_id=t1.id
)=0
Note: Above query will work perfectly if the ID in table C and ID in table F is not same and rel_id column in table oui is having unique values.

Query to show one column multiplied

I have the following 3 tables;
Table_Names:
user_id Name
------------------
1 Mark
2 Tom
3 Ana
Table_Language:
language_id Language
-----------------------
1 English
2 German
Table_Name_Lang
id user_id language_id
---------------------------
1 1 1
2 1 2
3 2 1
4 3 2
How can I create a query to show the expected results like those below?
Name Expr_1_Eng Expr_1_Ger
---------------------------------
Mark English German
Tom English
Ana German
Thanks Tok
I would do:
select n.name,
max(case when l.language = 'English' then l.language end) as has_English,
max(case when l.language = 'German' then l.language end) as has_German
from names n join
name_lang nl
on nl.user_id = n.user_id join
lang l
on nl.language_id = l.language_id
group by n.name
You can try to use JOIN with condition aggregate function
SELECT Name,
MAX(CASE WHEN tnl.language_id = 1 then tl.Language end),
MAX(CASE WHEN tnl.language_id = 2 then tl.Language end)
FROM
Table_Name_Lang tnl
JOIN Table_Names tn on tnl.language_id = tn.language_id
JOIN Table_Language tl on tl.user_id = tnl.user_id
GROUP BY Name
I think you should use pivot:
select n.name,[0] as language_one,[1] as language_two,[2],[3] from
(select n.name,tl.language
tablename n
left join Table_Name_Lang tnl on n.userid=tnl.userid
left join table_language tl on tl.id=tnl.id
group by n.name,tl.language
)T
pivot
(
language
for name in [0],[1],[2],[3]
)AS PivotTable;

Trying to GROUP BY with CASE WHEN THEN - postgresql

I have three tables PATH, ELEMENTS and ELEMENT_DETAILS with the following structure:
PATH:
ID
1
2
3
ELEMENTS:
ID | PATH_ID | DIRECTION | ELEMENT_DETAILS_ID
1 1 'left' 1
2 1 'right' 2
3 2 'left' 3
4 2 'right' 2
ELEMENT_DETAILS:
ID | NAME
1 'Henry'
2 'Mark'
3 'John'
I would like the result to be like so:
ID | left | right
1 'Henry' 'Mark'
2 'John' 'Mark'
This is the SQL I have come up with so far:
SELECT path.id,
CASE WHEN elements.direction='left' THEN element_details.name
ELSE NULL END
as left,
CASE WHEN elements.direction='right' THEN element_details.name
ELSE NULL END
as right,
FROM elements
INNER JOIN path on elements.path_id = path.id
LEFT JOIN element_details on elements.element_details_id = element_details.id
GROUP BY path.id
ORDER BY path.id
However, this does not work since postgres gives me an error saying elements.direction should be in group by. And including elements.direction in group_by does not give me aggregation at path.id level.
Stuck on this. Can someone please help.
I am using Postgres version 9.5
You need aggregation. Here is one method:
SELECT p.id,
MAX(CASE WHEN e.direction = 'left' THEN ed.name
END) as left,
MAX(CASE WHEN e.direction = 'right' THEN ed.name
END) as right
FROM elements e INNER JOIN
path p
ON e.path_id = p.id LEFT JOIN
element_details ed
ON e.element_details_id = ed.id
GROUP BY p.id
ORDER BY p.id