web-dev-qa-db-ja.com

PostgreSQL-日時範囲が重複しています

日時フィールドstartおよびendのテーブルがあります。 (スタート、エンド)アイテムのリストがあります。リストのどのアイテムがテーブルのデータと重複しているかを確認する必要があります。現在のクエリは次のようになります。

select br.duration from booking, (
    select tstzrange('2016-09-06 03:45:00+00', '2016-09-06 14:45:00+00') as duration 
    union select tstzrange('2016-09-06 14:45:00+00', '2016-09-06 15:45:00+00') as duration
    -- other items from my list
) as br 
where tstzrange(start, end) && br.duration

それを行う他の方法はありますか?テーブルに数百万の行があり、それらをリストの数百のアイテムと比較すると、うまくいくと思いますか?

4
Anna

100万行を処理するためのいくつかの重要な改善を提案します。

_SELECT br.duration
FROM  (
   VALUES 
      ('[2016-09-06 03:45:00+00, 2016-09-06 14:45:00+00)'::tstzrange)  
    , ('[2016-09-06 14:45:00+00, 2016-09-06 15:45:00+00)')
      -- more items
   ) br(duration)
WHERE EXISTS (
   SELECT FROM booking
   WHERE  tstzrange(ts_start, ts_end) && br.duration
   );
_
  • 値のリストに不必要に冗長で高価な形式_SELECT ... UNION ..._を提供しながら、それを_UNION ALL_にしてください。そうしないと、Postgresが重複を折り畳むために時間を浪費します。また、SELECTクエリの最初のUNIONの列名とデータ型を宣言するだけで済みます。
    しかし、VALUES式の方が単純で高速です。または、配列_tstzrange[]_を提供し、unnest()を使用します。

  • クエリは、bookingの重複する行ごとに1行を返しますが、おそらくリストから重複する各値onceが必要になる可能性があります。 DISTINCTまたは_GROUP BY_を追加して一意の行を取得することもできますが、それでも時間の無駄になります。 EXISTSセミジョインは、ケースの非常にシンプルで安価な代替手段の1つです:durationの各行重複するエントリが見つかった場合に1回だけ返され、Postgresはこの行の検索を停止できます。

  • インデックスのサポートがないと、クエリはまだ遅くなります。機能的な要旨または SP-Gist index を作成します。後者はおそらく最高のパフォーマンスを発揮します。

    _CREATE INDEX booking_ts_range_idx on booking USING spgist (tstzrange(ts_start, ts_end));
    _

関連:

5