我可以根据没有匹配项来创建Active Record关系

时间:2019-05-15 10:36:38

标签: ruby-on-rails ruby activerecord

我正在尝试在具有很多n + 1个查询的Rails应用程序上加快索引页面的速度。

目前,我有一种基本上可以反转关系的方法(查找与当前记录不相关的记录)

Class Person < ActiveRecord::Base
    has_many_and_belongs_to_many :bankholidays, join_table: “working_bankholidays”

    def free_bankholidays
        Bankholiday.where(“id NOT IN (?)” bankholiday_ids)
    end
end
Class Bankholiday < ActiveRecord::Base
    has_many_and_belongs_to_many :persons, join_table: “working_bankholidays”
end
Class WorkingBankholiday < ActiveRecord::Base
    belongs_to :person
    belongs_to :bankholiday
end

Person内部的free_bankholidays方法返回与当前Person无关的任何Bankholiday。这可以正常工作,但是如果我要由多个人加载索引页,则速度会变慢。有什么方法可以将其移至恋爱关系中,从而使其渴望加载?

1 个答案:

答案 0 :(得分:0)

您需要的是向外联接,然后选择外部联接创建NULL的结果。

您想要的SQL是:

SELECT bankholidays.*
FROM bankholidays
  LEFT OUTER JOIN working_bankholidays on bankholidays.id = working_bankholidays.bankholiday_id
  AND working_bankholidays.person_id = 111
WHERE working_bankholidays.id is NULL

这里的问题是,您需要 person_id 子句中FROM的条件。如果在WHERE子句中,您将得到不同的结果。

当它位于FROM子句中时,它将使用条件处理working_bankholidays表,就好像它只有具有匹配的person_id的行一样。这样做是在进行外部联接之前进行的,因此,如果没有匹配person_id的记录,它将添加来自外部联接的记录,并且working_bankholidays表的所有字段中的值均为NULL。

不幸的是,对于活动记录关联,我不知道有什么方法可以做到这一点。所有活动记录关联都将条件置于WHERE子句中,而不是FROM子句中。

如果将has_many_and_belongs_to_many更改为具有很多贯穿,则可以执行以下操作:

class Person
  def free_bankholidays
    Bankholiday.left_joins(:working_bankholidays).where(working_bankholidays: {id: nil, person_id: self.id})
  end

它将为您提供:

SELECT bankholidays.*
FROM bankholidays
  JOIN working_bankholidays on bankholidays.id = working_bankholidays.bankholiday_id
WHERE working_bankholidays.id is NULL
  AND working_bankholidays.person_id = 111

这将为您带来不同且错误的结果。

编辑:

请参阅Engineersmnky的评论。我还没有尝试过,所以还没有准备好将其包含在答案中。