web-dev-qa-db-ja.com

PostgreSQLが数値とbigint列を比較するときにseqスキャンを実行するのはなぜですか?

vp型のtimestamp型のテーブルbigintbtreetimestampインデックスを指定した場合、Postgresがインデックスを無視し、timestampと浮動小数点値の比較でseqスキャンを実行して、インデックススキャンで同じ結果が生成されるのはなぜですか?

整数比較:

_SELECT * FROM vp WHERE vp.timestamp > 1470752584_がかかる48ミリ秒

 vpのvp_ts_idxを使用したインデックススキャン(コスト= 0.57..257.87行= 2381幅= 57)(実際の時間= 0.014..38.669行= 80323ループ= 1)
インデックス条件:( "タイムスタンプ"> 1470752584) 
合計実行時間:48.322ミリ秒

数値比較:

_SELECT * FROM vp WHERE vp.timestamp > 1470752584.1_は_vp_ts_idx_を無視してテーブル全体のseqスキャンを実行するため、103秒かかります。

  vpのシーケンススキャン(コスト= 0.00..7378353.16行= 95403915幅= 57)(実際の時間= 62625.420..103122.701行= 98240ループ= 1)
フィルター:(( "timestamp"):: numeric> 1470752584.1)
フィルターによって削除された行数:285945491 
合計実行時間:103134.333 ms

コンテキスト:最近の車両位置のクエリは、timestampEXTRACT(Epoch FROM NOW()) - %sと比較しました。ここで、_%s_は、bigintに明示的にキャストせずに、必要な秒数でした。回避策は、CAST(EXTRACT(Epoch FROM NOW()) - %s AS bigint)を使用することです。

列の型がbigintの場合、クエリプランナーはこれを自動的に行わないのはなぜですか?これはバグですか、それともこの動作が役立つエッジケースを考慮していませんか?

4
Miles Erickson

重要なのは、同じタイプを比較しないことです。 bigintnumericと比較する場合、簡単な方法は、bigintを「展開」して小数点以下を0にして(1-> 1.0のように)、他の方法はそれを回避する方法は、丸め/切り捨てを意味します。 (この特定のケースでは、両方が同じ結果につながることが簡単にわかりますが、値が負の場合はどうなりますか?)

したがって、比較で得られるのはnumericからnumericへの比較であり、bigintインデックスが提供できるものではありません。

どのキャストが可能で、これらのケースで実行されるかは一見の価値があります。このため、ここに pg_cast の2つの行があります。

SELECT castsource::regtype, casttarget::regtype, castcontext 
  FROM pg_cast 
 WHERE castsource::regtype = 'bigint'::regtype
   AND casttarget::regtype = 'numeric'::regtype;

 castsource │ casttarget │ castcontext 
────────────┼────────────┼─────────────
 bigint     │ numeric    │ i

SELECT castsource::regtype, casttarget::regtype, castcontext
  FROM pg_cast
 WHERE castsource::regtype = 'numeric'::regtype
   AND casttarget::regtype = 'bigint'::regtype;

 castsource │ casttarget │ castcontext 
────────────┼────────────┼─────────────
 numeric    │ bigint     │ a

リンクされたドキュメントページによると、castcontext

キャストを呼び出すことができるコンテキストを示します。eは、明示的なキャストとしてのみ意味します(CASTまたは::構文を使用)。 aは、ターゲット列への割り当てで暗黙的に、および明示的に意味します。 iは、他の場合と同様に、式で暗黙的に意味します。

つまり、これは、前者を後者に割り当てた場合にのみ、numeric-> bigintの方向が「単独で」発生する(つまり、キャスト演算子の1つを明示的に呼び出さない)ことを意味します。比較のような式では、これは当てはまりません。そのため、パーサーは逆の方法(上記のiでマークされている)のみを考慮します。つまり、強制しない限り、numericからnumericへの比較が行われます。

注:

  • psqlはPostgreSQLのコマンドラインクライアントであり、それ自体はこれらのことを何も行いません(それに応じてタイトルを編集しました)。
  • キーワードを列名として使用すること(timestampなど)は決して良い考えではありません。あらゆる場所でキーワードを二重引用符で囲むように注意しない限り、あちこちで予期しない解析エラーが発生する可能性があります
  • unixエポックをタイムスタンプとして使用するのは面倒な場合があります。 PostgreSQLには「実際の」タイムスタンプの豊富な機能があります-ほとんどの場合、それを使用する方がはるかに簡単です。
4
dezso