找到所有子记录都具有给定值的所有父记录(但不仅仅是一些子记录)

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

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解决方案持开放态度。

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

1a)Forall x:p(x)

在SQL中必须转换为:

1b)不存在x:NOT​​ 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 ) 

即没有参与者的任何特定事件,状态!=’现在’

这样做的另一个主要方式是将参与者的数量与存在状态的参与者的数量进行比较

 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 ) 

两种解决方案都未经过测试,因此可能存在错误,但它应该为您提供一个开始

我非常喜欢Lennarts的例子

我对第一个示例进行了简单修改,该示例仅返回具有Participation Child记录的EVENT父记录,并且处理速度比查找每个记录的计数快得多。

 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 

如果您尝试通过查找某个人的状态不是“存在”的事件的ID来查询,然后找到所有唯一事件,那么该怎么办?

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

您可以使用子选择来过滤掉参与者不存在的事件。 不过,它可能不是最有效的方法。

 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 

我需要一个解决方案,其他答案对我不起作用,但这是我的解决方案。 我编写了两个函数,一个用于获取子记录的总数,另一个用于获取在我的情况下满足特定条件的子记录的总数(true)。 然后我比较了这两个function。 如果得到的算术/评估等于零,则表示所有记录都符合真实标准。 相当简单。

 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