一尘不染

查找所有子记录都具有给定值的所有父记录(而不只是某些子记录)

sql

一个活动有很多参与者。参与者的字段为“状态”。

class Event < ActiveRecord::Base
  has_many :participants
end

class Participant < ActiveRecord::Base
  belongs_to :event
end

我需要找到除以下事件以外的所有事件:每个参与者的状态都为“出席”的事件。

我可以找到带有以下AR代码的某些参与者处于“呈现”状态的所有事件:

Event.joins(:participants).where
 .not(participants: {status: 'present'})
  .select("events.id, count(*)")
   .group("participants.event_id")
    .having("count(*) > 0")

这样就创建了SQL:

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

几乎 可行。问题是,如果参与者的某行(在范围内
@participant.event_id)的状态为“离开”,则该事件仍将被获取,因为至少某些同级记录的状态与其他状态相同而不是“现在”。

我需要确保我正在过滤所有事件记录, 所有 参与者的状态均为“出席”。

我愿意接受ActiveRecord或SQL解决方案。


阅读 174

收藏
2021-03-10

共1个答案

一尘不染

如果我做对了,您的问题可以归类为关系分裂。基本上有两种方法可以解决此问题:

1a)全部x:p(x)

在SQL中必须将其转换为:

1b)不存在x:不存在p(x)

对于您的问题,可能是这样的:

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

即,任何不存在参与者的给定事件,使得状态!=’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 )

两种解决方案都未经测试,所以那里可能有错误,但这应该可以帮助您入门

2021-03-10