我有一个大约100个用户的表,我也有一个用户ID数组。我想要做的是显示所有不属于此用户ID数组的用户。当我做这样的事情时
User.where('id NOT IN (?)', [9, 2, 3, 4])
它成功返回用户id不属于该数组的记录。但是,如果该数组是空的,那么
User.where('id NOT IN (?)', [])
它不会返回任何用户,SQL查询看起来像这样
SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
有谁知道为什么会这样,或者这可能是一个错误?我正在使用Rails 3.2.5和PostgreSQL。
答案 0 :(得分:31)
在Rails 4中,您可以使用User.where.not(id: [])
来获得正确的结果。它产生:
SELECT "users".* FROM "users" WHERE (1 = 1)
不幸的是User.where('id NOT IN (?)', [])
应该是等价的,但事实并非如此。它仍然会给你错误的结果:
SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
参考文献:
答案 1 :(得分:24)
ActiveRecord(至少3.2.1)将空数组视为NULL。 where
来电中的占位符由sanitize_sql
处理。如果你追溯一下代码,你会来replace_bind_variables
:
def replace_bind_variables(statement, values) #:nodoc:
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
bound = values.dup
c = connection
statement.gsub('?') { quote_bound_value(bound.shift, c) }
end
def quote_bound_value(value, c = connection) #:nodoc:
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
c.quote(nil)
else
value.map { |v| c.quote(v) }.join(',')
end
else
c.quote(value)
end
end
空数组将满足所有四个条件,以便您进入c.quote(nil)
,这就是您的NULL来自的地方。导致c.quote(nil)
的所有特殊逻辑表明这是故意行为。
用空列表说IN(或NOT IN):
where c in ()
应该产生一个SQL错误,所以也许AR人试图通过悄悄地将那个坏的SQL转变为c in (null)
来阻止它。请注意,这些都不是:
select ... from t where c in (null);
select ... from t where c not in (null);
由于SQL的NULL行为,应该产生任何结果。这是一个经典的新手错误,AR人真的应该知道更好。
我自己也更喜欢一个例外:告诉我,我即将部署一只脚弹,会比给我一把不同的枪更友好。
执行摘要:
where('c in (?)', [])
或where('c not in (?)', [])
,因为这两种陈述都没有多大意义。答案 2 :(得分:6)
User.where('id NOT IN (?)', ids+[0])
答案 3 :(得分:1)
Use ruby's active record wrapper:
User.where.not(id: [])
This handles the empty array issue for you.
答案 4 :(得分:1)
我不知道这是否是问题所在,但我来到这里是为了找到所有带有空(序列化)数组属性的记录。我为Rails 5.0解决了这个问题:
User.where(my_array_attribute: nil)
或反过来:
User.where.not(my_array_attribute: nil)