我有一个类似的查询:
Tag.where('id not IN (?)', current_user.tags.pluck(:id)).uniq
当
current_user.tags.pluck(:id)).uniq
返回NULL,我没有得到Tag查询的结果,这不是所希望的行为。
我在这里做错了什么?
感谢。
答案 0 :(得分:3)
我认为current_user.tags.pluck(:id)
不会给你一个nil
,它会返回一个空数组。在这种情况下ActiveRecord will treat an empty array as a NULL。结果是一些荒谬的SQL,如下所示:
select tags.* from tags where id in (null)
由于SQL的NULL(特别是x = NULL
和x != NULL
的特性对于所有x
都是假的),因此in (null)
或not in (null)
WHERE子句不匹配任何内容。
将Ruby的[]
转换为NULL
的Rails非常愚蠢(关于over here的更多讨论),但即使它足够聪明地引发异常,你仍然需要处理手动使用“空数组”案例,如下所示:
tag_ids = current_user.tags.pluck(:id)
if(tag_ids.empty?)
tags = Tag.all
else
tags = Tag.where('id not in (?)', tag_ids)
end
并且您不需要uniq
,SQL in
运算符会将其RHS视为一组,因此重复项将在幕后折叠。
答案 1 :(得分:1)
where.not
让Rails处理。由于 mu太短指出,当传递给查询条件的值是一个空数组时,ActiveRecord会将其转换为NULL
,这会打乱您的查询,并将始终返回一个空结果。
处理此问题的旧方法是有条件地检查一个空数组作为参数,而不添加此条件。但是,使用引入了where.not
的Rails 4,我们不再需要进行此检查。
我们可以简单地做到:
Tag.where.not( id: current_user.tags.pluck(:id) ).uniq
现在ActiveRecord将自动检查一个空数组,当它看到它时,条件变为1=1
,这实际上是没有意义的,但更重要的是,它只会被忽略,其余查询将像该条件从未添加到查询中。