检索具有空数组的记录时出现问题

时间:2012-10-18 01:57:44

标签: ruby-on-rails ruby-on-rails-3 postgresql activerecord arel

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

5 个答案:

答案 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

然后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代码以检查空数组并执行任何需要执行的操作以获得您期望的结果。

答案 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)
相关问题