带 rails activerecord 的左外连接和 where 子句

Left outer join and where clause with rails activerecord

我有两个模型:User 和 Rsvp。用户有很多 Rsvps。

我想获取在指定日期没有创建 Rsvp 的所有用户。

所以我写了这个:

scope :free, -> (date) { joins(:rsvps).where.not(rsvps: {created_at: date.beginning_of_day..date.end_of_day}) }

但它仅在用户至少有一个 Rsvp 而不是 'date' 时才有效。当用户根本没有 Rsvp 时,它不起作用。

我尝试使用左外连接,但它不起作用。我一定错过了什么 :

scope :free, -> (date) { joins("LEFT OUTER JOIN rsvps ON rsvps.user_id = users.id").where.not(rsvps: {created_at: date.beginning_of_day..date.end_of_day}) }

这是我的单元测试:

it 'should fetch user if he has no rsvp at all' do
  # Given
  user = User.create!(email: 'm@m.com', password: '12345678', password_confirmation: '12345678')

  # Then
  expect(User.free(Date.new(2014, 1, 1)).count).to eq 1
end

通常 joins returns 用户至少有一个 Rsvp。这就是为什么我建议使用 includes。试试这个(可能你必须修改条件,因为我不确定它是否正是你想要的):

scope :free, -> (date) { includes(:rsvps).where('rsvps.created_at != ?', date ).references(:rsvps) }

你几乎做对了。您需要使用 GROUP_BY 语句,这样您就不会得到重复的行。

scope :free, -> (date) do 
  joins("LEFT OUTER JOIN rsvps ON rsvps.user_id = users.id")
    .where.not(rsvps: {created_at: date.beginning_of_day..date.end_of_day})
    .group("users.id") # or just .group(:id)
end

这应该有效: scope :free, -> (date) { includes(:rsvps).where("rsvps.id IS NULL OR rsvps.created_at NOT BETWEEN ? AND ?", date.beginning_of_day, date.end_of_day).references(:rsvps) }

希望对您有所帮助!