Postgres/RailsでのOR条件の最適化を無効にする方法

col col2 という列を持つ A というモデルがあります。 col1 には一意のインデックスがあります。 (私はPostgresデータベースを使用しています。)

class A < ActiveRecord::Base
  # id, col1, col2
end

データ:

id  col1   col2
==  ====   ====
1   2       3
2   3       4

col2 OR col1 で、指定された値と一致する最初の行を見つけようとします。

a = A.first(:conditions=> ["col2 = :id OR col1 = :id", {:id => 3}])
# select * from A where (col2 = 3 OR col1 = 3) LIMIT 1

上記のSQLは、行#1ではなく行2を返します。私は、 col1 がユニークなインデックスを持つため、クエリオプティマイザが最初に col1 = 3 の実行を選択したと考えます。

この動作をどのように無効にするのですか? PostgresオプティマイザにOR条件として既存の注文を使用するように指示するにはどうすればよいですか?

2
面白い....
追加された 著者 daniel,

2 答え

明示的な順序を指定しないと、最初の行は意味をなさない。特定の瞬間の特定のクエリからの結果セットはもちろん最初の行を持ちますが、同じデータを使って同じクエリを再度実行すると同じ最初の行が生成されるという保証はありません。リレーショナルデータベースのテーブルは順序付けされていないので、指定された既存の順序はまったくありません。あなたは悪い仮定をしています。実装は暗黙的にディスク上のPKによってレコードを順序付けすることがありますが、それを保証するものではなく、次のバージョンが同じように動作するという保証はありません。さらに、データベースがディスク順序で行を返すという保証はなく、ORDER BY句で特定の順序を指定しないかぎり、データベースは自由に行を並べ替えることができます。

注文が必要な場合は、ORDER BY句を含める必要があります。あなたはおそらくこのような何かをしたい:

a = A.where("col2 = :id OR col1 = :id", {:id => 3}).order(:id).first

そして、 col1 の前に col2 を見たい場合は、

a =  A.where('col2 = :id', :id => 3).order(:id).first \
  || A.where('col1 = :id', :id => 3).order(:id).first

col2 = :id OR col1 = :id and col1 = :id OR col2 = :id are equivalent boolean expressions so the database is free to check col2 = :id before, after, or at the same time as col1 = :id and it is free to access the rows in any order it pleases.

5
追加された
@ダニエル:私は、列の順序についても保証はないと思いますが、データベースはWHERE句を自由に並べ替えることができます(結果が同じである限り)。あなたが何か特定の順序で起こることを望むなら、手でそれを整理しなければなりません。
追加された 著者 mu is too short,
+1、私の現在のソリューションでは、あなたが提案したもの(つまり、結果をパイプする)と似ています。私は2行戻っているかもしれないという事実を考えなかった。明らかだったはずです。ありがとう。
追加された 著者 Harish Shetty,
これは、MySQLと比較してPostgresで特に当てはまります
追加された 著者 Wizard of Ogz,
あなたはまだcol2 =を保証することができません:idが最初に評価されます。それでも可能ですcol1 =:idは最初に正しく評価できますか?
追加された 著者 daniel,

ケースステートメントを使用することはできません:

http://www.postgresql.org/docs /current/static/sql-expressions.html#SYNTAX-EXPRESS-EVAL

この方法で使用されるCASE構文は、最適化の試行を無効にするので、必要なときにのみ行うべきです。

1
追加された