我期待编写一个范围,返回所有没有特定关联的记录。
foo.rb
class Foo < ActiveRecord::Base
has_many :bars
end
bar.rb
class Bar < ActiveRecord::Base
belongs_to :foo
end
我想要一个范围,可以找到不拥有Foo's
的所有bars
。使用joins
很容易找到具有关联的那些,但我还没有找到相反的方法。
答案 0 :(得分:37)
Rails 4使这太容易了:)
Foo.where.not(id: Bar.select(:foo_id).uniq)
这会输出与jdoe的答案相同的查询
SELECT "foos".*
FROM "foos"
WHERE "foos"."id" NOT IN (
SELECT DISTINCT "bars"."foo_id"
FROM "bars"
)
作为范围:
scope :lonely, -> { where.not(id: Bar.select(:item_id).uniq) }
答案 1 :(得分:18)
我有100 array_multisort(array_column($myArray, 'count'), SORT_DESC, $myArray);
和9900 foos
。 bars
中的99个每个都有100个foos
,其中一个没有。{/ p>
bars
生成一个SQL查询:
Foo.left_outer_joins(:bars).where(bars: { foo_id: nil })
并返回Foo Load (2.3ms) SELECT "foos".* FROM "foos" LEFT OUTER JOIN "bars" ON "bars"."foo_id" = "foos"."id" WHERE "bars"."foo_id" IS NULL
,但没有Foo
当前接受的答案bars
不正在运作。它产生两个SQL查询:
Foo.where.not(id: Bar.select(:foo_id).uniq)
返回所有Bar Load (8.4ms) SELECT "bars"."foo_id" FROM "bars"
Foo Load (0.3ms) SELECT "foos".* FROM "foos" WHERE ("foos"."id" IS NOT NULL)
,因为所有foos
的{{1}}都不为空。
需要将其更改为foos
以将其缩减为一个查询并找到我们的id
,但它在基准测试中效果不佳
Foo.where.not(id: Bar.pluck(:foo_id).uniq)
答案 2 :(得分:13)
在foo.rb
class Foo < ActiveRecord::Base
has_many :bars
scope :lonely, lambda { joins('LEFT OUTER JOIN bars ON foos.id = bars.foo_id').where('bars.foo_id IS NULL') }
end
答案 3 :(得分:9)
我更喜欢使用squeel gem来构建复杂的查询。它以如此神奇的方式扩展了ActiveRecord:
Foo.where{id.not_in Bar.select{foo_id}.uniq}
构建以下查询:
SELECT "foos".*
FROM "foos"
WHERE "foos"."id" NOT IN (
SELECT DISTINCT "bars"."foo_id"
FROM "bars"
)
所以,
# in Foo class
scope :lonely, where{id.not_in Bar.select{foo_id}.uniq}
是您可以用来构建请求范围的。
答案 4 :(得分:5)
将NOT EXISTS与LIMIT-ed子查询一起使用可以更快:
SELECT foos.* FROM foos
WHERE NOT EXISTS (SELECT id FROM bars WHERE bars.foo_id = foos.id LIMIT 1);
使用ActiveRecord(&gt; = 4.0.0):
Foo.where.not(Bar.where("bars.foo_id = foos.id").limit(1).arel.exists)
答案 5 :(得分:0)
此方法利用includes
并允许范围链接。它应该与Rails 5+一起使用
scope :barless, -> {
includes(
:bars
).where(
bars: {
id: nil
}
)
}