Rails如何从指向同一个表的连接关联中进行选择

时间:2018-03-08 15:22:36

标签: sql ruby-on-rails ruby join activerecord

假设我有一个与协会相关的模型突发事件员工

事件

belongs_to :recipient, class_name: 'Employee', foreign_key: :recipient_id
belongs_to :responder, class_name: 'Employee', foreign_key: :responder_id

我希望在事件模型中有一个范围,可以在收件人和响应者的名称之间进行检索。

scope :report, -> {left_joins(:recipient, :responder).select('incidents.*', 'responder.name AS Responder Name', 'recipient.name AS Recipient Name')}

我尝试过这样的事情,但我总是得到“缺少FROM子句......”的错误。

我需要了解的是:

如何在select中引用已加入的关联,以便我可以distingush 响应者和收件人之间是同一个表的记录,但是根据不同的外键来表示?

2 个答案:

答案 0 :(得分:3)

加入时需要对表进行别名:

SELECT 
  incidents.*, 
  recipient.name AS recipient_name, 
  responder.name AS responder_name 
FROM "incidents" 
LEFT JOIN 
  users recipient ON recipient.id = incidents.recipient_id 
LEFT JOIN 
  users responder ON responder.id = incidents.responder_id

您还需要为所选列使用适当的别名 - "收件人姓名"不能很好地映射到ruby方法名称。

class Incident < ApplicationRecord
  belongs_to :recipient, class_name: 'User'.freeze
  belongs_to :responder, class_name: 'User'.freeze

  def self.report
    self.select('
      incidents.*,
      recipient.name AS recipient_name,
      responder.name AS responder_name
    ')
        .joins("LEFT JOIN users recipient ON recipient.id = incidents.recipient_id")
        .joins("LEFT JOIN users responder ON responder.id = incidents.responder_id")
  end
end
irb(main):028:0> Incident.report.map(&:attributes)
  Incident Load (1.4ms)  SELECT incidents.*, recipient.name AS recipient_name, responder.name AS responder_name FROM "incidents" LEFT JOIN users recipient ON recipient.id = incidents.recipient_id LEFT JOIN users responder ON responder.id = incidents.responder_id
=> [{"id"=>1, "recipient_id"=>1, "responder_id"=>2, "created_at"=>Thu, 08 Mar 2018 15:51:52 UTC +00:00, "updated_at"=>Thu, 08 Mar 2018 15:51:52 UTC +00:00, "recipient_name"=>"Bob", "responder_name"=>"Erika"}]

答案 1 :(得分:1)

我只使用includes代替joins并使用默认的getter方法而不是select

Incidents.includes(:recipient, :responder).each do |incident|
  puts incident
  puts incident.recipient.name
  puts incident.responder.name
end