find all parent records where all child records have a given value (but not just some child records)

断了今生、忘了曾经 提交于 2019-11-30 16:57:51

If I get it right your problem can be classified as relational division. There are basically two ways to approach it:

1a) Forall x : p(x)

which in SQL has to be translated to:

1b) NOT Exists x : NOT p(x)

For your problem that would be something like:

SELECT e.* 
FROM events e
WHERE NOT EXISTS (
    SELECT 1 
    FROM PARTICIPANTS p
    WHERE p.status <> 'present'
      AND p.event_id = e.event_id
)

i.e. any given event where there does not exist a participant such that status != 'present'

The other principle way of doing it is to compare the number of participants with the number of participants with status present

SELECT e.id 
FROM events e
JOIN participants p 
    ON p.event_id = e.id 
GROUP BY e.event_id 
HAVING count(*) = count( CASE WHEN p.status = 'present' then 1 end )

Both solutions are untested so there might be errors in there, but it should give you a start

I really like Lennarts examples

I made a simple modification to the first example which will only return EVENT parent records which have Participation Child records, and is much faster at processing than finding the counts for each.

SELECT e.* 
FROM events e
INNER JOIN participants p ON p.event_id = e.event_id
WHERE NOT EXISTS (
  SELECT 1 
  FROM PARTICIPANTS p
  WHERE p.status <> 'present'
  AND p.event_id = e.event_id
)
GROUP BY e.event_id

What if you try to come at the query by finding the ids of the events where a person has a status other than "present" and then find all the unique event where that is the case?

unique_event_ids = Participant.where.not(status: "present").pluck(:event_id).uniq
events_you_want = Event.where(unique_event_ids)

You could use a subselect to filter out events that have participants that aren't present. It's probably not the most efficient way to do it, though.

SELECT events.id, participants.status as status, count(*) 
FROM `events` INNER JOIN `participants` 
ON `participants`.`event_id` = `events`.`id` 
WHERE (`participants`.`status` != 'present')
AND events.id NOT IN (SELECT DISTINCT event_id FROM participants WHERE participants.status != 'present')
GROUP BY participants.event_id HAVING count(*) > 0
Ted

I need a solution for this and the other answers didn't work for me, but here is my solution. I wrote two functions one to get the total number of child records and another to get the total number of child records that meet a particular condition in my case (true). Then I compared both functions. If the resulting arithmetic/evaluation equal zero it means all records meet the true criteria. Quite straightforward.

Select p.pid, p.Name, p.Group, udfn_TotalChildrenRecords(p.pid), udfn_TotalChildrenRecordsThatAreTrue(p.pid) 
From Parent AS p INNER JOIN Child AS c ON Parent.pid = child.pid
GROUP BY p.pid, p.Name, p.Group
HAVING udfn_TotalChildrenRecords(p.pid) - udfn_TotalChildrenRecordsThatAreTrue(p.pid) = 0
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!