web-dev-qa-db-ja.com

範囲演算子で使用するTIMESTAMP列にインデックスを作成する

[〜#〜] tldr [〜#〜]:次のWHERE句で使用されるインデックスを作成できます:

WHERE foo_date <@ tsrange('2018-01-01', '2018-02-01')

 CREATE TABLE foo 
(
 foo_id INTEGER GENERATED BY DEFAULT AS IDENTITY、
 foo_date timestamp without time zone NOT NULL、
 CONSTRAINT foo_pkey PRIMARY KEY( foo_id)
); 

このテーブルには、日付が2009-01-01から2018-12-29の100,000件のレコードが含まれています。特定の日付範囲の行をクエリできるようにしたい(例:2018年1月の行)。

オプション1

1つの方法は、BETWEEN演算子を使用することです。

 SELECT * FROM foo WHERE foo_date BETWEEN '2018-01-01' AND '2018-01-31'; 

このアプローチの問題は、foo_dateが深夜0時以降に2018-01-31で発生した場合、それらがこのクエリに含まれないことです。したがって、クエリをBETWEEN '2018-01-01' AND '2018-02-01'に変更できます。ただし、問題、次には、2018-02-01 00:00:00で発生するレコードです。これらは含まれますが、必要ありません。

オプション2

Aaron Bertrand によって提示された別のオプションは、この構成を使用することです:

foo_date >= '2018-01-01' AND foo_date < '2018-02-01'

(はい、このブログはSQL Serverに関するものですが、ここで適用できるようです)。

この形式では、希望どおりの結果が得られますが、面倒です。列名を2回繰り返す必要があります。

オプション3

Postgresは範囲データ型を提供するので、より明確な形式は次のようになると思いました:

foo_date <@ tsrange('2018-01-01', '2018-02-01') 

次の質問は、このフォームを使用する場合、インデックスを使用して操作を高速化できるかどうかです。


CREATE INDEX idx_foo ON foo(foo_date);

オプション1または2を使用するクエリはインデックスを使用します。

EXPLAIN SELECT * FROM foo 
WHERE 
    foo_date >= '2018-01-01' 
    AND foo_date < '2018-02-01';

このクエリプランを教えてください:

Bitmap Heap Scan on foo  (cost=21.95..592.70 rows=942 width=12)
  Recheck Cond: ((foo_date >= '2018-01-01 00:00:00'::timestamp without time zone) AND (foo_date < '2018-02-01 00:00:00'::timestamp without time zone))
  ->  Bitmap Index Scan on idx_foo  (cost=0.00..21.71 rows=942 width=0)
        Index Cond: ((foo_date >= '2018-01-01 00:00:00'::timestamp without time zone) AND (foo_date < '2018-02-01 00:00:00'::timestamp without time zone))

ただし、オプション3を使用する場合、インデックスは使用されません。

EXPLAIN SELECT * FROM foo 
WHERE foo_date <@ tsrange('2018-01-01', '2018-02-01');

私に与える:

Seq Scan on foo  (cost=0.00..1791.00 rows=500 width=12)
  Filter: (foo_date <@ '["2018-01-01 00:00:00","2018-02-01 00:00:00")'::tsrange)

Gistインデックスを作成しようとすると、最初にエラーメッセージが表示されます。

ERROR:  data type timestamp without time zone has no default operator class for access method "Gist"
HINT:  You must specify an operator class for the index or define a default operator class for the data type.
SQL state: 42704 

btree_Gist拡張子を追加した後、インデックスを作成できます。

CREATE INDEX idx_foo ON foo USING Gist (foo_date)

ただし、@>または<@を使用しても、インデックスは使用されません。

行方不明のものはありますか?それとも、この構造で使用できるインデックスを作成するのは現実的ではありませんか?

1
Zack
  1. 名前 "foo_date" dateを示し、timestamp列には不適切です。 オプション1 は、実際の dates で問題なく機能します。

  2. オプション2 is と組み合わせたプレーンなbtreeインデックス間違いなく最良のソリューション。これ以上探さない。物理的にソートされたデータを持つ大きなテーブルのBRINインデックスの特別な場合を除いて。見る:

コンセプトの証明

つまり、GistまたはSP-Gistインデックスを機能させるには、偽の範囲に expression index を作成します。モジュールは必要ありませんbtree_Gist このため。 SP-Gistを使用してデモンストレーションします。これは通常、ここで少し高速になるためです。見る:

CREATE INDEX foo_date_spgist_idx ON foo USING spgist(tsrange(foo_date, foo_date, '[]'));

SELECT * FROM foo
WHERE  tsrange(foo_date, foo_date, '[]') <@ tsrange('2018-01-01', '2018-02-01')

または範囲リテラル:

...
WHERE  tsrange(foo_date, foo_date, '[]') <@ '[2018-01-01,2018-02-01)'

But:btreeインデックスよりも大きく、維持にコストがかかり、低速です。書くのもそれほど面倒ではありません。あなたの場合には無意味です。

余談: technically 、次のことができます:

... WHERE foo_date BETWEEN '2018-01-01' AND '2018-01-31 23:59.999999';

Postgresのタイムスタンプタイプは、(現在)µsの解像度で実装されています。小数6桁。したがって、式はまさにあなたが望むものを行います。しかし、この実装の詳細に基づいて構築することは強くお勧めしません。オプション2はその方法です。関連:

2