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.
Related
I have 2 tables:
categories
id | name | | slug | path | parent_id | depth
1 name1 slug1 {1} null 0
2 name2 slug2 {1,2} 1 1
3 name3 slug3 {1,2,3} 2 2
5 nam5 slug5 {5} null 0
......
9 nam4 slug9 {5,9} 5 1
where path is an int[]array type and works like a breadcrumb
items
id | name
1 name1
Between Item and Category there is a M2M relation
item_categories
id | item_id | category_id
1 1 | 3
2 1 9
In the example above the Item is in 3 categories:
I use the following SQL:
SELECT c1.id, c1.name, c1.slug, c1.parent_id FROM categories AS c1
WHERE ARRAY[c1.id] <# (SELECT c2.path FROM categories AS c2 WHERE c2.id=
(SELECT category_id FROM item_categories WHERE item_id=8 LIMIT 1)) order by depth
to extract a breadcrumb base on path and it works.
But I want to get all breadcrumbs(not just one). Removing LIMIT 1 and changing = to in I will have an array of arrays, instead of just an array, and will trigger an error:
more than one row returned by a subquery used as an expression
which is normal.
Example of what I want - Item in:
cat1 - > cat2 - >cat3
ca5 -> cat9
, and from database(so I can loop over them):
[ [{'name':cat1, 'slug':slug1}, {'name':cat2, 'slug':slug2}, {'name':cat3, 'slug':slug3}], [{'name':cat5, 'slug':slug5}, {'name':cat9, 'slug':slug9}]]
dbfiddle: https://dbfiddle.uk/?rdbms=postgres_10&fiddle=f756cfe568d38425dfe25cfec60b1b3f
So instead of obtaining one breadcrumb how can I get an array o breadcrumbs as result ?
using json_build_object, unnest and ordered json_agg:
select
c.id,
json_agg(
json_build_object('name',c2.name,'slug',c2.slug)
order by p.depth
)
from categories as c
inner join lateral unnest(c.path) with ordinality as p(id, depth) on true
inner join categories as c2 on
c2.id = p.id
where
exists (
select *
from item_categories as tt
where
tt.item_id = 1 and
tt.category_id = c.id
)
group by
c.id
db<>fiddle demo
Or you can use depth column from you table if you want:
select
c.id,
json_agg(
json_build_object('name',c2.name,'slug',c2.slug)
order by c2.depth
)
from categories as c
inner join categories as c2 on
c2.id = any(c.path)
where
exists (
select *
from item_categories as tt
where
tt.item_id = 1 and
tt.category_id = c.id
)
group by
c.id
db<>fiddle demo
What I don't like about json_build_object is that you have to name your columns explicitly doing double work, so I've tried to useto_json instead. It works, but honestly, I'm not that familiar with this syntax when alias of the table is passed to the function as an argument (see Using Composite Types in Queries) and could not make it work without lateral join:
select
c.id,
json_agg(to_json(d) order by c2.depth)
from categories as c
inner join categories as c2 on
c2.id = any(c.path)
cross join lateral (select c.name, c.slug) as d
where
exists (
select *
from item_categories as tt
where
tt.item_id = 1 and
tt.category_id = c.id
)
group by
c.id
db<>fiddle demo
I have three tables A,B and C.
A and B are connected a foreign key A.category_id=B.id ,
A and C have the same number of rows.
A
id category_id value1 value2
1 null 'A' null
2 null 'B' null
3 null 'C' null
4 null 'D' null
B
id category
1 0
2 1
C
id category
1 0
2 1
3 1
4 0
Expected result:
A
id value
1 1
2 2
3 2
4 1
I would like to receive updated A table where category_id will be id from table B based on table C category.
I have tried
UPDATE A SET
A.category_id = (
select B.id from A
left JOIN C
ON A.id = C.id
left join B on B.category=C.category
)
WHERE A.id IN (SELECT C FROM C WHERE A.id = C.id);
but then I received ORA-01427 single-row subquery returns more than one row tips
You are missing a correlation clause in the subquery:
update A
set category_id = (select B.id
from C join
B
on B.category = C.category
where A.id = C.id
)
where exists (select 1 from C where A.id = C.id);
To correlated a query, you refer to the outer reference typically in a where condition. You don't repeat the table reference in the from clause.
Note that an outer join is unnecessary. If there is no match, the value will be NULL.
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)
I'm trying to write a code that will join together information from 3 tables and compare it with a "big table" and according to the comparison update a field in a test table. Look at the example:
Big Table:
Employee_Id status
2322 5
222 3
545 6
4532 2
Table 1:
S_Id status
2322 7
Table 2:
S_Id status
222 3
Table 3:
S_Id status
545 6
4532 3
Test Table:
TE_Id IsNotGood
2322 1
4532 1
222 0
545 0
The id "2322" got 1 because the status in table 1 is not like the status in the big table and same for 4532.
I started writing this:
update Test Table
set isNotGood = 0
with ids as (
select distinct Employee_Id from (
select Employee_Id,Status from BigTable as B
) as T
inner join table1 as W on W.S_ID = T.Employee_Id
inner join table2 as K on K.S_ID = T.Employee_Id
inner join table3 as R on R.S_ID = T.Employee_Id
)
I would be happy for your tips to finish this query.
Thank you very much!
You won't be able to use an inner join unless the employee_id is in all the tables.
UPDATE Test
SET isNotGood = 1
WHERE te_id in (select employee_id
from [Big Table] bt
left join table1 as W on W.s_id = bt.employee_Id
left join table2 as K on K.s_id = bt.employee_Id
left join table3 as R on R.s_id = bt.employee_Id
where (bt.status <> W.status and W.status is not null) or (bt.status <> K.status and k.status is not null) or (bt.status <> R.status and R.status is not null);
I have a table like this
Table A:
Id Count
1 4
1 16
1 8
2 10
2 15
3 18
etc
Table B:
1 sample1.file
2 sample2.file
3 sample3.file
TABLE C:
Count fileNumber
16 1234
4 2345
15 3456
18 4567
and so on...
What I want is this
1 sample1.file 1234
2 sample2.file 3456
3 sample3.file 4567
To get the max value from table A I used
Select MAX (Count) from A where Id='1'
This works well but my problem is when combining data with another table.
When I join Table B and Table A, I need to get the MAX for all Ids and in my query I dont know what Id is.
This is my query
SELECT B.*,C.*
JOIN A on A.Id = B.ID
JOIN C on A.id = B.ID
WHERE (SELECT MAX(COUNT)
FROM A
WHERE Id = <what goes here????>)
To summarise, what I want is Values from Table B, FileNumber from Table c (where the count is Max for ID from table A).
UPDATE: COrrecting table C above. Looks like I need Table A.
I think this is the query you're looking for:
select b.*, c.filenumber from b
join (
select id, max(count) as count from a
group by id
) as NewA on b.id = NewA.id
join c on NewA.count = c.count
However, you should take into account that I don't get why for id=1 in tableA you choose the 16 to match against table C (which is the max) and for id=2 in tableA you choose the 10 to match against table C (which is the min). I assumed you meant the max in both cases.
Edit:
I see you've updated tableA data. The query results in this, given the previous data:
+----+---------------+------------+
| ID | FILENAME | FILENUMBER |
+----+---------------+------------+
| 1 | sample1.file | 1234 |
| 2 | sample2.file | 3456 |
| 3 | sample3.file | 4567 |
+----+---------------+------------+
Here is a working example
Using Mosty’s working example (renaming the keyword count to cnt for a column name), this is another approach:
with abc as (
select
a.id,
a.cnt,
rank() over (
partition by a.id
order by cnt desc
) as rk,
b.filename
from a join b on a.id = b.id
)
select
abc.id, abc.filename, c.filenumber
from abc join c
on c.cnt = abc.cnt
where rk = 1;
select
PreMax.ID,
B.FileName,
C2.FileNumber
from
( select C.id, max( C.count ) maxPerID
from TableC C
group by C.ID
order by C.ID ) PreMax
JOIN TableC C2
on PreMax.ID = C2.ID
AND PreMax.maxPerID = C2.Count
JOIN TableB B
on PreMax.ID = B.ID