Zac*_*ack 4 postgresql index timestamp range-types
TLDR:我可以创建一个由以下WHERE子句使用的索引吗:
WHERE foo_date <@ tsrange('2018-01-01', '2018-02-01')
Run Code Online (Sandbox Code Playgroud)
创建表 foo
(
foo_id INTEGER 由默认身份生成,
不带时区的 foo_date 时间戳 NOT NULL,
约束 foo_pkey 主键 (foo_id)
);
此表包含 100,000 条记录,日期从2009-01-01到2018-12-29。我希望能够查询给定日期范围内的行(例如 2018 年 1 月的行)。
一种方法是使用BETWEEN运算符:
SELECT * FROM foo WHERE foo_date BETWEEN '2018-01-01' AND '2018-01-31';
这种方法的问题是,如果foo_date发生在2018-01-31午夜之后,它们将不会包含在此查询中。所以我可以将查询更改为BETWEEN '2018-01-01' AND '2018-02-01'. 那么问题来了,然而,上发生的记录2018-02-01 00:00:00。这些将被包括在内,这是我不想要的。
Aaron Bertrand提出的另一种选择是使用这个结构:
foo_date >= '2018-01-01' AND foo_date < '2018-02-01'
Run Code Online (Sandbox Code Playgroud)
(是的,此博客适用于 SQL Server,但似乎适用于此处)。
虽然这种形式明确地为我提供了我想要的结果,但它很麻烦:我必须重复列名两次。
由于 Postgres 为我们提供了范围数据类型,我认为更清晰的形式可能是:
foo_date <@ tsrange('2018-01-01', '2018-02-01')
Run Code Online (Sandbox Code Playgroud)
所以我的下一个问题是,如果我使用这种形式,我可以使用索引来加快操作吗?
CREATE INDEX idx_foo ON foo(foo_date);
Run Code Online (Sandbox Code Playgroud)
使用选项 1 或 2 的查询将使用索引:
EXPLAIN SELECT * FROM foo
WHERE
foo_date >= '2018-01-01'
AND foo_date < '2018-02-01';
Run Code Online (Sandbox Code Playgroud)
给我这个查询计划:
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))
Run Code Online (Sandbox Code Playgroud)
但是,如果我使用选项 3,则不使用索引:
EXPLAIN SELECT * FROM foo
WHERE foo_date <@ tsrange('2018-01-01', '2018-02-01');
Run Code Online (Sandbox Code Playgroud)
给我:
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)
Run Code Online (Sandbox Code Playgroud)
如果我尝试创建一个要点索引,我最初会收到一条错误消息。
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
Run Code Online (Sandbox Code Playgroud)
添加btree_gist扩展后,我可以创建索引:
CREATE INDEX idx_foo ON foo USING gist (foo_date)
Run Code Online (Sandbox Code Playgroud)
但是,使用@>或<@仍然没有使用索引。
有什么我想念的吗?或者创建一个可以被这个构造使用的索引是不可行的?
名称“foo_date”表示 adate并且对于timestamp列来说是一个错误的选择。选项 1适用于实际日期。
一个简单的 btree 索引与选项 2 相结合是明确的最佳解决方案。别再看了。除了具有物理排序数据的大型表的 BRIN 索引的特殊情况。看:
也就是说,要使 GiST 或 SP-GiST 索引工作,您可以在假范围上创建表达式索引。您不需要此模块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')
Run Code Online (Sandbox Code Playgroud)
或使用范围文字:
...
WHERE tsrange(foo_date, foo_date, '[]') <@ '[2018-01-01,2018-02-01)'
Run Code Online (Sandbox Code Playgroud)
但是:更大,维护成本更高,比 btree 索引慢。写起来也不那么麻烦。对你的情况毫无意义。
旁白:从技术上讲,您可以:
... WHERE foo_date BETWEEN '2018-01-01' AND '2018-01-31 23:59.999999';
Run Code Online (Sandbox Code Playgroud)
Postgres 时间戳类型(当前)以 µs 分辨率实现,即最大。6 位小数。因此,该表达式完全符合您的要求。但我强烈建议不要建立在这个实现细节上。选项2是要走的路。有关的: