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 代码以检查空数组并做任何需要做的事情以获得您期望的结果。