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))

なぜこれが起こるのか、それともバグなのか誰か知っていますか? Rails 3.2.5 と PostgreSQL を使用しています。

4

5 に答える 5

32

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))

参考文献:

于 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

空の配列は、4 つの条件すべてを満たし、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 (?)', [])orを試してはいけません。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 に答える