web-dev-qa-db-ja.com

RailsのWHERE句でINの代わりにANYを使用するにはどうすればよいですか?

以前は次のようなクエリがありました。

MyModel.where(id: ids)

これは次のようなSQLクエリを生成します:

SELECT "my_models".* FROM "my_models"
WHERE  "my_models"."id" IN (1, 28, 7, 8, 12)

これを変更して、ANYの代わりにINを使用したいと思います。私はこれを作成しました:

MyModel.where("id = ANY(VALUES(#{ids.join '),('}))"

空の配列を使用するとids = []次のエラーが発生します:

MyModel Load (53.0ms)  SELECT "my_models".* FROM "my_models"  WHERE (id = ANY(VALUES()))
ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
Position: 75: SELECT "social_messages".* FROM "social_messages"  WHERE (id = ANY(VALUES()))
    from arjdbc/jdbc/RubyJdbcConnection.Java:838:in `execute_query'
11
Eki Eqbal

IN式には2つのバリエーションがあります。

同様に、ANY構造を持つ2つのバリアント:

サブクエリはどちらの手法でも機能しますが、それぞれのsecond形式の場合、IN値のリスト(標準SQLで定義)を期待しますが、_= ANY_はarrayを期待します。

どちらを使用しますか?

ANYは後の、より用途の広い加算であり、ブール値を返す任意の二項演算子と組み合わせることができます。 INANYの特別な場合に燃え尽きます。実際、その2番目の形式は内部で書き直されています。

INは_= ANY_で書き直されます
_NOT IN_は_<> ALL_で書き直されます

クエリのEXPLAIN出力を確認して、自分で確認してください。これは2つのことを証明します:

  • INは_= ANY_より速くなることはありません。
  • _= ANY_は大幅に高速化されることはありません。

選択は提供しやすいもの:値のリストまたは配列(おそらく配列リテラルとして-単一の値)によって決定する必要があります。

渡すIDが来た場合DB内からとにかく、IDを直接選択するか(サブクエリ)、ソーステーブルをJOINでクエリに統合する方がはるかに効率的です。 ( @ muコメント のように)。

クライアントから長いリストの値を渡し、最高の値を取得するにはパフォーマンス =、配列unnest()を使用して結合するか、VALUESを使用してテーブル式として提供します( @ PinnyMコメント など)。ただし、JOINは、提供された配列/セットで可能なduplicatesを保持しますが、INまたは_= ANY_は保持しないことに注意してください。もっと:

NULL値が存在する場合、_NOT IN_はしばしば間違った選択であり、_NOT EXISTS_は正しい(そしてより高速でもある)でしょう:

_= ANY_の構文

配列式の場合、Postgresは以下を受け入れます。

無効な型キャストを回避するために、明示的にキャストできます。

_ARRAY[1,2,3]::numeric[]
'{1,2,3}'::bigint[]
_

関連:

または、 could VARIADICパラメータを使用してPostgres関数を作成します。この関数は、個々の引数を受け取り、それらから配列を形成します。

Rubyから配列を渡す方法は?

idintegerと仮定します。

_MyModel.where('id = ANY(ARRAY[?]::int[])', ids.map { |i| i})
_

しかし、私はRubyに手を出しているだけです。 @muは、この関連する回答で詳細な手順を提供します。

40