I have a strange result when performing a lateral join on a query
I have the following table structure
task->id
comment -> id , taskId, comment
tasklink -> taskId, type, userid
with a single task record (id 10), 1 comment record ("row1", "a test comment") and 5 tasklink records (all with taskid 10)
I expected this query
select task.id,
json_agg(json_build_object('id',c.id, 'user',c.comment)) as comments,
json_agg(json_build_object('type',b.type, 'user',b.userid)) as users
FROM task
left join lateral (select c.* from comment c where task.id = c.taskid) c on true
left join lateral (select b.* from taskuserlink b where task.id = b.taskid) b on true
where task.id = 10
GROUP BY task.id ;
to return
id | comments | users
---------------------------------------------------------------------
10 "[{"id":"row1","user":"a test comment"}]" "[{"type":"updatedBy","user":1},"type":"closedBy","user":5},"type":"updatedBy","user":5},"type":"createdBy","user":5},{"type":"ownedBy","user":5}]"
instead, I got this
id | comments | users
10 "[{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"}]" "[{"type":"updatedBy","user":1},{"type":"closedBy","user":5},{"type":"updatedBy","user":5},{"type":"createdBy","user":5},{"type":"ownedBy","user":5}]"
ie , for every link row, the comment row is duplicated
I am thinking that I am missing something really obvious, but as I have only just started using Postgres (and sql ) I'm a little stumped
I would appreciate some guidance on where I'm going wrong
Move the aggregates into subqueries:
select id, comments, users
from task t
left join lateral (
select json_agg(json_build_object('id',c.id, 'user',c.comment)) as comments
from comment c
where t.id = c.taskid
) c on true
left join lateral (
select json_agg(json_build_object('type',b.type, 'user',b.userid)) as users
from taskuserlink b
where t.id = b.taskid
) b on true
DbFiddle.
Related
I have four tables, three of which are pretty static: haul_types, dumpster_type_team (the dumpster_type_team has the many-to-many relationship between dumpster_types and teams), and users. The fourth table, hauls, has transactional data.
haul_types:
id
name
dumpster_type_team:
id
dumpster_type_id
team_id
users:
id
first_name
last_name
is_driver
team_id
hauls:
haul_type_id
haul_status_id
set_dumpster_type_id
completed_driver_id
team_id
I would like a query that has a combination of dumpster_types, haul_types, and drivers (users) and a count of the hauls they were involved in. In some cases, there should be a count of zero because some drivers haven't completed hauls for every haul_type / dumpster type combination.
Here's the query I have so far that seems to be behaving as if it is an inner join because the records are getting filtered to only show where there are matches:
SELECT
c.haul_type_id,
c.dumpster_type_id,
c.driver_id,
count(h.id) AS haul_count
FROM
hauls h
RIGHT JOIN ( SELECT DISTINCT
ht.id AS haul_type_id,
dtt.dumpster_type_id AS dumpster_type_id,
dtt.team_id AS team_id,
u.id AS driver_id
FROM
haul_types ht
CROSS JOIN dumpster_type_team dtt
CROSS JOIN users u
WHERE
u.team_id = dtt.team_id
AND u.is_driver = TRUE) c ON c.haul_type_id = h.haul_type_id
AND c.dumpster_type_id = h.set_dumpster_type_id
AND c.driver_id = h.completed_driver_id
AND c.team_id = h.team_id
WHERE
h.team_id = 9
AND h.haul_status_id = 3
AND h.completed_driver_id IS NOT NULL
GROUP BY
c.haul_type_id, c.dumpster_type_id, c.driver_id
When I run the subquery in isolation:
SELECT DISTINCT
ht.id AS haul_type_id,
dtt.dumpster_type_id AS dumpster_type_id,
dtt.team_id AS team_id,
u.id AS driver_id
FROM
haul_types ht
CROSS JOIN dumpster_type_team dtt
CROSS JOIN users u
WHERE
u.team_id = dtt.team_id
AND u.is_driver = TRUE
I get the results I want: a row for each permutation of haul_type, dumpster_type, driver_id, and team_id. However, when I run the entire query, I get filtered results despite the right join.
What I would like to have is the following:
If I have 4 haul_types: delivery, swap, live, pickup
and 2 dumpster_types: 10YD, 15YD
and 2 drivers: 1, 2
I would like a haul count for the combination of haul_type, dumpster_type, and driver. If there are no hauls matching the row, show 0:
Any help is appreciated. Thank you
The description of the question and the query seem to have little to do with each other. I don't know what a "pivot table" is supposed to be.
I would like a query that has a combination of dumpster_types, haul_types, and drivers (users) and a count of the hauls they were involved in.
This sounds like a cross join to generate the rows and then a left join/group by to calculate the results:
select d.dumpster_id, ht.haul_type_id, d.driver_id, count(h.driver_id)
from dumpster_types d cross join
haul_types ht cross join
drivers d left join
hauls h
on h.dumpster_id = d.dumpster_id and
h.haul_type_id = ht.haul_type_id and
h.driver_id = d.driver_id
group by d.dumpster_id, ht.haul_type_id, d.driver_id;
Running the query #GordonLinoff provided, exposed the issue I was facing - when applying a where clause on the top level query, the results were getting filtered to only matches. I moved the where clause to individual subqueries and now I am getting all expected results.
Not sure if this is the most efficient way to write it but it yields the correct results:
SELECT
d.dumpster_type_id,
ht.id AS haul_type_id,
u.id AS driver_id,
count(h.id) AS haul_count
FROM (
SELECT
dumpster_type_id,
team_id
FROM
dumpster_type_team
WHERE
team_id = 9) d
CROSS JOIN haul_types ht
CROSS JOIN (
SELECT
users.id
FROM
users
WHERE
users.is_driver = TRUE
AND users.team_id = 9) u
LEFT JOIN (
SELECT
id, set_dumpster_type_id, haul_type_id, completed_driver_id, team_id
FROM
hauls
WHERE
haul_status_id = 3
AND team_id = 9) h ON h.set_dumpster_type_id = d.dumpster_type_id
AND h.haul_type_id = ht.id
AND h.completed_driver_id = u.id
AND h.team_id = d.team_id
GROUP BY
d.dumpster_type_id,
ht.id,
u.id
I am trying to get a row of another table as a sub-query of the SELECT query.
SELECT g.ip, g.version, t.testid, t.status, m.rundate, t.runid , (select * from B where runid=t.runid and testid=t.testid)
FROM Tests t, Master m, Env g
WHERE t.runid=m.runid
AND t.runid=g.runid
AND g.version = "1.2.3"
AND t.testid like "%test_name%"
AND g.ip in ("198.18.111.222")
order by m.rundate desc;
How can this be achieved?
Thanks!
Try with this:
SELECT g.ip, g.version, t.testid, t.status, m.rundate, t.runid , c.* from B c
Join Tests t on t.runid =t.testid
join Master m on t.runid=m.runid
join Env g on t.runid=g.runid
WHERE g.version = "1.2.3" AND t.testid like "%test_name%"
AND g.ip in ("198.18.111.222")
order by m.rundate desc;
at line 2 if there is any id in B table which matches with the id in Tests
replace t.runid = t.testid (suppose if you have runid in B table which relates to test table testid then replace with c.runid = t.testid)to the requirement in which the B table and tests table are satisfying the scenario of matching ID's(PK and FK).
In MySQL, I have a two tables as below :
ClientTable
clientID clientName
1 Client A
2 Client B
3 Client C
4 Client D
5 Client E
6 Client F
NotesTable
noteID clientID note noteDate
1 3 Test 1 12-Jun-14
2 3 Test 2 18-Aug-14
3 4 Test 3 23-Oct-14
4 6 Test 4 25-May-14
5 3 Test 5 25-Nov-14
6 6 Test 6 16-Jul-14
I want to select all the clients from the client table and, where a note exists for the client, the date of the latest note entry. If no note exists for a client, then return null for the noteDate. Desired result set as follows :
client ID clientName latestNoteDate
1 Client A null
2 Client B null
3 Client C 25-Nov-14
4 Client D 23-Oct-14
5 Client E null
6 Client F 16-Jul-14
Any help appreciated, I have tried a few options using nested Select with MAX(noteDate) and various left joins but can't seem to get it right.
Why all the subqueries?
select ct.clientID, ct.clientName,max(nt.noteDate) latestNoteDate
from ClientTable ct
left outer join NotesTable nt
on ct.clientID = nt.clientID
group by ct.clientID, ct.clientName
You can use an outer join with a subquery:
select c.clientid, c.clientname, n.latestnotedate
from client c
left join (
select clientId, max(noteDate) latestnotedate
from notes
group by clientId
) n on c.clientId = n.clientId
This assumes the max(noteDate) is the latest note entry. If that's not the case, easy enough to use the noteid instead and then just include one additional join.
Looks like a good place for using a sub-query. Try something like:
select c.id, c.name, n.latestNoteDate from client c
left join
(select clientid, MAX(notedate) as latestNoteDate from note
group by clientid) as n on n.clientid = c.id
The key is, find the data you want from the notes table first, then use that to join with the client data later.
Try the following code, which uses a correlated sub-query.
SELECT ct.clientID,
ct.clientName,
(SELECT MAX(noteDate)
FROM notesTable nt
WHERE nt.clientID = ct.clientId)
FROM clientTable ct
select clienttable.clientID, clienttable.clientName, notes.noteDate
left outer join NotesTable notes on notes.clientID = clienttable.clientID and noteDate = (select max(noteDate) from NotesTable where notes.clientID = clienttable.clientID)
It will return null if there are no note entries.
OR
select clienttable, clientID, clienttable.clientName, (select max(noteDate) from NotesTable where notes.clientID = clienttable.clientID) noteDate
I have a short access/mySQL question. I have a mapping table on the format below.
ID Category_A Category_B Category_C Team
1 a b T1
2 a d T2
I have a second table which also includes Category_A, Category_B, and Category_C. I would like to join the Team value to the my second table based on the mappingtable. My problem is that when there is a blank (e.g. ID=2, Category_B) the mapping should assign the T2 to any row that contains Category_A=a and Category_C=d regardless of the value in Category_B.
Can this type of mapping be done?
Grateful for your help!
In MS Access, I think you would need something on the lines of:
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_C = t.Category_C)
AND (m.Category_B = t.Category_B)
AND (m.Category_A = t.Category_A)
WHERE m.Category_C Is Not Null
AND m.Category_B Is Not Null
AND m.Category_A Is Not Null
UNION ALL
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_B = t.Category_B)
AND (m.Category_A = t.Category_A)
WHERE m.Category_C Is Null
AND m.Category_B Is Not Null
AND m.Category_A Is Not Null
UNION ALL
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_C = t.Category_C)
AND (m.Category_A = t.Category_A)
WHERE m.Category_C Is Not Null
AND m.Category_B Is Null
AND m.Category_A Is Not Null
UNION ALL
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_C = t.Category_C)
AND (m.Category_B = t.Category_B)
WHERE m.Category_C Is Not Null
AND m.Category_B Is Not Null
AND m.Category_A Is Null
i have a details table with columns:
user_id int
code int
value int
And i want to build a summary table that looks like:
user_id int
valueA int
valueB int
In the details table, valueA would correspond to say, code 5, and valueB would correspond to say, code 6, so i'm looking for something like:
insert into summary (user_id,valueA,valueB) VALUES ( SELECT ??? from details );
The problem of course is that i'm looking at multiple rows from the "details" table to populate one row in the "summary" table.
Eg, if i had the following rows in details:
1 5 100
1 6 200
2 5 1000
2 6 2000
I want to end up with the following in the summary table:
1 100 200
2 1000 2000
Any ideas?
MySQL doesn't have PIVOT/UNPIVOT syntax, which leaves you to use a combination of GROUP BY and CASE expressions:
INSERT INTO SUMMARY
(user_id,valueA,valueB)
SELECT d.user_id,
MAX(CASE WHEN d.code = 5 THEN d.value ELSE NULL END),
MAX(CASE WHEN d.code = 6 THEN d.value ELSE NULL END),
FROM DETAILS d
GROUP BY d.user_id
insert into summary (user_id,valueA,valueB)
SELECT a.user_id, a.value, b.value
from details a
join details b on a.user_id = b.user_id
WHERE a.code = 5 and b.code = 6;
beware: you will end up with multiple summary columns if user_id+code is not unique.
EDIT:
insert into summary (user_id,valueA,valueB)
select u.user_id, ifnull(a.value,0), ifnull(b.value,0)
from (select distinct user_id from details /* where code in (5,6) */) u
left join details a on a.user_id = u.user_id and a.code = 5
left join details b on b.user_id = u.user_id and b.code = 6
If you have a manageable set of codes (say just 5 and 6) you could do something like this:
SELECT details.user_id, code5.value, code6.value
FROM details JOIN
(SELECT user_id, value FROM details WHERE code = 5) AS code5 USING(user_id)
JOIN
(SELECT user_id, value FROM details WHERE code = 6) AS code6 USING(user_id);
You may need to modify your JOINs depending on if your codes are not required as 1 to 1 relationship (i.e. LEFT JOINs).
If you have a large set of codes, I would look into a cursor runs a similar query above over a result set of your codes or using a different technology, (i.e. PHP script).