I have 3 tables:
entities: list of entities, some of them can participate to an event;
events: list of the events;
participations: list of participations
Here are the structures:
ENTITIES
id | name
EVENTS
id | name | date_start | date_end
PARTICIPATIONS
id | id_event | id_participation | type
I'd like to get all the entities, with in the column "events" the array of all the events. Here is what gives me a good result:
SELECT
e.id,
e.name,
array_agg(
json_build_object(
'id', evts.id,
'name', evts.name,
'date_start', evts.date_start,
'date_end', evts.date_end
)
) as events
FROM g.entities e
LEFT JOIN public.events_participations p
ON (
p.id_participation::integer = e.id
AND type ='entity'
)
LEFT JOIN public.events evts
ON evts.id = p.id_event
WHERE e.id = 22
GROUP BY p.id_participation, e.id
BUT (and there is a but otherwise I wouldn't write here) when an entity doesn't participate in any event, I have this in the column:
{"{\"id\" : null, \"name\" : null, \"date_start\" : null, \"date_end\" : null}"}
I thought I could get a "NULL" because of the LEFT JOIN.
I tried to use "CASE" to detect when there are no event, but it doesn't seem to be the solution. How could I get the expected result and get an array only if there is at least one event and NULL otherwise?
Thanks in advance.
EDIT:
I tried this:
WITH list_events AS (
SELECT
e.id,
json_build_object(
'id', evts.id,
'name', evts.name,
'date_start', evts.date_start,
'date_end', evts.date_end
) as events
FROM g.entities e
LEFT JOIN public.events_participations p
ON (
p.id_participation::integer = e.id
AND type ='entity'
)
JOIN public.events evts
ON evts.id = p.id_event
WHERE e.id = 1
)
SELECT
e.id,
e.nom,
array_agg(
l.*
) as events
FROM g.entities e
LEFT JOIN list_events l
ON e.id = l.id
WHERE e.id = 1
GROUP BY e.id
But instead of having "NULL" I have en empty column of type "record[]".
I was almost there:
WITH list_events AS (
SELECT
e.id,
json_build_object(
'id', evts.id,
'name', evts.name,
'date_start', evts.date_start,
'date_end', evts.date_end
) as events
FROM g.entities e
LEFT JOIN public.events_participations p
ON (
p.id_participation::integer = e.id
AND type ='entity'
)
JOIN public.events evts
ON evts.id = p.id_event
WHERE e.id = 1
)
SELECT
e.id,
e.nom,
array_agg(
l.events
) as events
FROM g.entities e
LEFT JOIN list_events l
ON e.id = l.id
WHERE e.id = 1
GROUP BY e.id
Related
I have this query:
with users_having_connected as (
select u.id as user_id,
(a.connected_at is not null) has_connected
from core_user u
join core_profile p on u.id = p.user_id
join core_conversation c on (c.profile1_id = p.id or c.profile2_id = p.id)
join analytics_connection a on c.id = a.conversation_id
group by u.id, (a.connected_at is not null)
)
select u.id as user_id,
date_trunc('month', u.created at time zone 'UTC')::date as month,
p.community_id,
p.organization_id,
p.profile_type_intention,
(p.basic_account_completed and (p.is_mentor or p.is_entrepreneur)) as profile_is_completed,
exists(select 1 from core_message where core_message.sender_id = p.id) as has_sent_a_message,
(EXISTS (SELECT 1 FROM core_message WHERE core_message.receiver_id = p.id)) AS has_received_a_message,
(EXISTS (SELECT 1 FROM core_admin_conversation_w_resp WHERE core_admin_conversation_w_resp.initiator_id = p.id or core_admin_conversation_w_resp.responder_id = p.id)) AS has_one_by_one,
exists(select 1 from users_having_connected where user_id = u.id) as has_two_by_two
from core_user as u
join core_profile p on u.id = p.user_id
where
p.profile_type_intention is not null
Which worked fine until I added this line:
(EXISTS (SELECT 1 FROM core_admin_conversation_w_resp WHERE core_admin_conversation_w_resp.initiator_id = p.id or core_admin_conversation_w_resp.responder_id = p.id)) AS has_one_by_one,
This specific variable add causes this query to infinite loop. What do I need to do to fix it? Am I missing a set of parentheses somewhere?
From here, and here I have figured out that if I want to aggregate a set of related rows into an array of objects I have to use this syntax:
(select to_json(C) from ( /* subquery */ ) C)
So, if I have three tables: user, creature and their junction table user_creature:
And I want to retrieve each user, and each creature that belongs to this user, I would have to do something like this:
select to_json(T)
from (
select "user".id as user_id,
(select to_json(C) -- !!! There it is
from (
select name, height
from creature
inner join "user_creature" uc on creature.id = "uc".creature_id
inner join "user" u on "uc".user_id = u.id
where u.id = user_id
) C) as "creatures" -- !!! There it is
from "user"
) T;
This query successfully retrieves a list of users and their related creatures:
Is there a way to drop select and from keywords from the query, so that I can write my query like this:
select to_json(T)
from (
select "user".id as user_id,
to_json( -- !!! Calling to_json directly on select statement
select name, height
from creature
inner join "user_creature" uc on creature.id = "uc".creature_id
inner join "user" u on "uc".user_id = u.id
where u.id = user_id
) as "creatures"
from "user"
) T;
It is possible to use a subquery as the argument to to_json, but not practical:
You need to wrap the subquery in a grouping parenthesis: to_json( (SELECT … FROM …) )
The subquery must return exactly one row (but that's normal)
The subquery must return exactly one column. This is a bit harder - you can return a record, but if you build it dynamically (e.g. from a selection of columns, you can hardly control the field names)
(See a demo here).
Instead, use json_build_object if you want to write a single SELECT query only:
SELECT json_build_object(
'user_id', u.id,
'creatures', (
SELECT json_build_object(
'name', c.name,
'height', c.height
)
FROM creature c
INNER JOIN "user_creature" uc ON c.id = uc.creature_id
WHERE uc.user_id = u.id
)
)
FROM "user" u;
And, if you want to be able to retrieve multiple rows use SELECT json_agg(json_build_object(…)) FROM … or ARRAY(SELECT json_build_object(…) FROM …):
SELECT json_build_object(
'user_id', u.id,
'creatures', (
SELECT json_agg(json_build_object(
'name', c.name,
'height', c.height
))
FROM creature c
INNER JOIN "user_creature" uc ON c.id = uc.creature_id
WHERE uc.user_id = u.id
)
)
FROM "user" u;
How can I write a linq to entities query that includes a group by and a having clause?
For example in SQL:
SELECT * FROM dbo.tblParent p
INNER JOIN
(
SELECT a.ID
FROM dbo.tblParent a
join dbo.tblChild c ON a.ID = c.FkParentID
WHERE a.ColValue = 167
GROUP BY A.ID
HAVING COUNT(c.ID) = 1
) t ON p.ID = t.ID
I found my own answer.
// this is far from pretty but it works as far as I can tell
Apps = (from x in context.tblParents
join t in (
from p in context.tblParents
join c in context.tblChilds
on p.ID equals c.FkParentID
where p.ColValue == 167
group c.ID by p.ID into grouped
where grouped.Count() == 1
select new { grouped.Key }) on x.ID equals t.Key
select x);
I'm trying to pull a dataset that returns records ONLY when there are two QUALIFERs present. I've tried left joins, populating data in temp tables then manipulating something, then numerous having clauses (resulting in subquery selects, and additional groups). I would appreciate any assistance on what I can do further.
Query:
Select E, CASE WHEN QUALIFER = '1' THEN 'NAME1' WHEN QUALIFER = '2' then 'NAME2' ELSE 'FINALNAME' END AS TYPE, count(rt.ID) 'Number '
from TABLE_ONE co (nolock)
join TABLE_TWO rt (nolock)
on co.ID = rt.ID
where co.E in (select * from #tempEmail)
AND convert(date,co.INSERTED_TIMESTAMP)between '1/1/2020' and '8/15/2020'
AND TRANS_STATUS = 'APPROVED'
group by E, QUALIFER
order by E, QUALIFER
Current resultset:
E TYPE Number
FAKEEMAIL1#Gmail NAME1 1
FAKEEMAIL1#Gmail NAME2 1
otheremailj#gmail.com Name1 21
Desired resultset:
E TYPE Number
FAKEEMAIL1#Gmail NAME1 1
FAKEEMAIL1#Gmail NAME2 1
Thank you.
Let's try the below query. I used a temp table to make things more simple for my mind.
if object_id('tempdb.dbo.#email') is not null drop table #email
create table #email
(
email varchar(50),
typeValue varchar(15),
Number int
)
insert into #email(email, typeValue, Number)
Select
E,
CASE WHEN QUALIFER = '1' THEN 'NAME1' WHEN QUALIFER = '2' then 'NAME2' ELSE 'FINALNAME' END AS TYPE,
count(rt.ID) 'Number '
from TABLE_ONE co (nolock)
join TABLE_TWO rt (nolock)
on co.ID = rt.ID
where co.E in (select * from #tempEmail)
AND convert(date,co.INSERTED_TIMESTAMP)between '1/1/2020' and '8/15/2020'
AND TRANS_STATUS = 'APPROVED'
group by E, QUALIFER
select a.email, a.typeValue
from #email a
inner join
(
select email, typeValue, rank() over (partition by email order by typeValue) as typeCount
from #email t
) as b
on b.email = a.email
and b.typeCount > 1
I have a question regarding lateral joins in Postgres.
My use case is I want to return a dataset that combines multiple tables but limits the number of publications and reviews returned. The simplified table schema is below
Table Author
ID
NAME
Table Review
ID
AUTHOR_ID
PUBLICATION_ID
CONTENT
Table Publication
ID
NAME
Table AuthorPublication
AUTHOR_ID
PUBLICATION_ID
So for my initial query I have this:
SELECT
a.id,
a.name
json_agg (
json_build_object (
'id', r.id,
'content', r.content
)
) AS reviews,
json_agg (
json_build_object(
'id', p.id,
'name', p.name
)
) AS publications
FROM
public.author a
INNER JOIN
public.review r ON r.author_id = a.id
INNER JOIN
public.author_publication ap ON ap.author_id = a.id
INNER JOIN
public.publication p ON p.id = ap.publication_id
WHERE
a.id = '1'
GROUP BY
a.id
This returns the data I need, for example I get the author's name, id and a list of all of their reviews and publications they belong to. What I want to be able to do is limit the number of reviews and publications. For example return 5 reviews, and 3 publications.
I tried doing this with a lateral query but am running into an issue where if I do a single lateral query it works as intended.
so like:
INNER JOIN LATERAL
(SELECT r.* FROM public.review r WHERE r.author_id = a.id LIMIT 5) r ON TRUE
This returns the dataset with only 5 reviews - but if I add a second lateral query
INNER JOIN LATERAL
(SELECT ap.* FROM public.author_publication ap WHERE ap.author_id = a.id LIMIT 5) r ON TRUE
I now get 25 results for both reviews and publications with repeated/duplicated data.
So my question is are you allowed to have multiple lateral joins in a single PG query and if not what is a good way to go about limiting the number of results from a JOIN?
Thanks!
You must change your query to something like this:
SELECT
a.id,
a.name,
(
SELECT
json_agg ( r )
FROM (
SELECT
json_build_object (
'id', r.id,
'content', r.content
) AS r
FROM public.review r
WHERE r.author_id = a.id
ORDER BY r.id
LIMIT 5
) AS a
) AS reviews,
(
SELECT
json_agg (p)
FROM (
SELECT
json_build_object(
'id', p.id,
'name', p.name
) AS p
FROM public.author_publication ap
INNER JOIN public.publication p ON p.id = ap.publication_id
WHERE ap.author_id = a.id
ORDER BY p.id
LIMIT 3
) AS a
) AS publications
FROM
public.author a
WHERE
a.id = '1'