32

我有一个大约 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))

有谁知道为什么会发生这种情况,或者这可能是一个错误?我正在使用带有 PostgreSQL 的 Rails 3.2.5。

4

5 回答 5

32

在 Rails 4 中,您可以使用User.where.not(id: [])which 将为您提供正确的结果。它产生:

SELECT "users".* FROM "users" WHERE (1 = 1)

不幸的是User.where('id NOT IN (?)', [])应该是等价的,但事实并非如此。它仍然给你错误的结果:

SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))

参考:

于 2013-06-07T01:22:37.883 回答
25

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 人真的应该知道得更多。

我自己更喜欢一个例外:告诉我我将要部署一个脚子弹比只是给我一把不同的枪要友好得多。


执行摘要

  1. 这种“空数组意味着 NULL”的行为是故意的。
  2. 你永远不应该尝试where('c in (?)', [])where('c not in (?)', [])因为这两种说法都没有多大意义。
  3. 更新您的 Ruby 代码以检查空数组并做任何需要做的事情以获得您期望的结果。
于 2012-10-18T02:47:53.273 回答
9
User.where('id NOT IN (?)', ids+[0])
于 2016-09-13T14:34:29.737 回答
2

使用 ruby​​ 的活动记录包装器:

User.where.not(id: [])

这将为您处理空数组问题。

于 2016-10-20T13:12:04.663 回答
1

我不知道这是否是所要求的问题,但我来这里是为了找到所有具有空(序列化)数组属性的记录。我为 Rails 5.0 解决了这个问题:

User.where(my_array_attribute: nil)

或者反过来:

User.where.not(my_array_attribute: nil)
于 2017-03-12T10:05:31.280 回答