Rails 查找所有关联记录满足条件的位置

问题描述 投票:0回答:5

我正在尝试让所有与医生相关的客户,但他们都没有开始他们的第一次会议(一个客户有_许多医生,并且可以与他们每个人进行第一次会议)。

到目前为止我已经:

@clients =  Client.joins(:doctors).where('doctors.first_session IS NULL').order('clients.id DESC')

但是当客户有例如 2 名医生时,这不起作用。第一个 doctor.first_session = null 但第二个不是。这种情况会退回客户,但客户不希望这样。

有什么想法吗?

ruby-on-rails activerecord
5个回答
3
投票

这是一种情况,为了查找不满足特定条件的记录,您可以通过查找除满足条件的记录之外的所有记录来实现。在 SQL 中,这是通过 WHERE 子句中的子查询来完成的。

对于这样的情况,squeel gem 非常有用,因为它封装了 SQL 复杂性。这就是我要做的(用尖叫声):

scope :visited_doctor, joins(:doctors).where { doctors.first_visit != nil }
scope :not_visited_doctor, where { id.not_in(Patient.visited_doctor.select(:id)) }

请注意,您可以在不使用 squeel 的情况下完成此操作,但您必须亲自动手(和代码)使用 SQL。


0
投票

这可以工作,但效率可能会有点低,因为它在 ruby 中完成一些工作,而不是在数据库中完成所有工作。

clients = Client.order('clients.id DESC').include(:doctors).select do |client|
  client.doctors.all? {|doctor| doctor.first_session.nil? }
end

从逻辑上讲,应该获取所有客户端,然后在 ruby 中,选择将评估块中的条件,并且返回 true 的那些客户端将被分配给客户端。

仅当该客户的所有医生的first_session均为nil时,条件块才会返回true。

希望有帮助。可能有一种更有效的方法可以使用子选择来完成此操作,但其语法可能取决于您使用的数据库。


0
投票

好吧,我找到了一个涉及两个查询的解决方案。

avoid_ids_results = Doctors.select('client_id')
                        .where("first_session IS NOT NULL")
                        .map(&:client_id).join(', ')

@clients =  Clients.
              joins(:doctors).
              where('clients.id NOT IN (' + avoid_ids_results + ')').
              order('clients.id DESC')

谢谢大家!


0
投票

最近针对这个场景做了一个解决方案。不需要第三方库,但您需要连接到使用 Postgresql/其他支持

bool_and
函数的 SQL 风格的数据库:

scope :not_visited_doctor,
      -> do
        joins(:doctors)
          .having('every(doctors.first_visit IS NULL)')
          .group(:id)
          .order(id: :desc)
      end

every
bool_and
的别名)是一个聚合函数,如果所有非空输入值均为 true,则返回 true,否则返回 false (1)。

另请注意,符号可用于引用 ActiveRecord 中

group
order
子句中的列。


-4
投票

您可以在

Client
模型中创建一个方法,如果客户医生的任何first_session为真,该方法将返回true,例如...

def has_first?
  self.doctors.each do |doctor|
    return true if !doctor.first_session.nil?
  end
  return false
end

这是伪代码,可能需要先调整

© www.soinside.com 2019 - 2024. All rights reserved.