检索具有空数组的记录时出现问题
我有一个大约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。
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
然后quote_bound_value
:
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人真的应该知道更好。
我自己更喜欢一个例外:告诉我,我即将部署一个脚踩子弹会比给我一把不同的枪更友好。
执行摘要 :
- 这种“空数组意味着NULL”的行为是故意的。
- 你永远不应该尝试
where('c in (?)', [])
或where('c not in (?)', [])
因为这两个陈述都没有多大意义。 - 更新您的Ruby代码以检查空数组并执行任何需要执行的操作以获得您期望的结果。
在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))
参考文献:
User.where('id NOT IN (?)', ids+[0])
使用ruby的活动记录包装器:
User.where.not(id: [])
这会为您处理空数组问题。
我不知道这是否是问题所在,但我来到这里找到所有带有空(序列化)数组属性的记录。 我为Rails 5.0解决了这个问题:
User.where(my_array_attribute: nil)
或者反过来:
User.where.not(my_array_attribute: nil)