web-dev-qa-db-ja.com

空の配列を持つレコードを取得するときに問題が発生する

約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を使用しています。

31
Robert

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)に到達するための4つの条件すべてを満たし、そこから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コードを更新して、空の配列をチェックし、期待する結果を得るために必要なことをすべて実行します。
25
mu is too short

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

参照:

31
GuiGS
User.where('id NOT IN (?)', ids+[0])
6
Pilipenok Oleg

Rubyのアクティブレコードラッパーを使用します。

User.where.not(id: [])

これにより、空の配列の問題が処理されます。

2
Danielle

これが求められている問題であるかどうかはわかりませんが、空の(シリアル化された)配列属性を持つすべてのレコードを見つけるためにここに来ました。私はそれをRails 5.0のように解決しました:

User.where(my_array_attribute: nil)

またはその逆の場合:

User.where.not(my_array_attribute: nil)
1
Fellow Stranger