问题:检索空数组记录时的问题

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

解答

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”的行为是故意的。

  1. 你永远不应该尝试where('c in (?)', [])where('c not in (?)', []),因为这两种说法都没有多大意义。

  2. 更新您的 Ruby 代码以检查空数组并做任何需要做的事情以获得您期望的结果。

Logo

PostgreSQL社区为您提供最前沿的新闻资讯和知识内容

更多推荐