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

时间:2014-05-17 03:30:00

标签: sql ruby-on-rails ruby-on-rails-4 has-many belongs-to

一个活动有很多参与者。参与者有一个" status"的领域。

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范围内)具有其他类似&#34; away&#34;的状态,则该事件仍将被取出,因为在至少有些兄弟记录的状态等于&#34; present&#34;以外的其他记录。

我需要确保使用所有参与者过滤掉每个事件记录,状态为&#34;出现&#34;。

我对ActiveRecord或SQL解决方案持开放态度。

5 个答案:

答案 0 :(得分:5)

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

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 )

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

答案 1 :(得分:2)

我非常喜欢Lennarts examples

我对第一个示例做了一个简单的修改,它只返回具有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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

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