【发布时间】:2021-10-08 05:42:47
【问题描述】:
我有下表
1 id int4 NO NULL "nextval('users_id_seq'::regclass)" NULL
2 first_name varchar(100) YES NULL NULL NULL
3 last_name varchar(100) YES NULL NULL NULL
4 start timestamptz YES NULL NULL NULL
5 end timestamptz YES NULL NULL NULL
6 bio text YES NULL NULL NULL
我已尝试创建以下索引
create index foo on users (start);
create index foo2 on users ("start", "end");
我创建了 100 万个随机行。我想优化这个查询,或者至少我希望它使用一些索引
explain select * from "users" where "start" < '2021-09-24 01:00:00.000' and "end" > '2018-09-24 01:00:00.000'
explain select * from "users" where "start" < '2021-09-24 01:00:00.000' and '2021-08-24 01:00:00.000' < "end" ;
输出是
Gather (cost=1000.00..50520.72 rows=52278 width=238)
Workers Planned: 2
-> Parallel Seq Scan on users (cost=0.00..44292.92 rows=21782 width=238)
" Filter: ((start < '2021-09-24 01:00:00+00'::timestamp with time zone) AND ('2021-08-24 01:00:00+00'::timestamp with time zone < ""end""))"
它在 2.5 秒内运行,但是,我的目标并不是真正优化速度。我对索引的工作方式更感兴趣。具体来说,我可以看到它使用顺序扫描。我很好奇什么样的索引实际上适合这样的查询。据我所知,B+Tree 在这种情况下并没有真正的帮助,因为实际上不可能将日期范围有效地编码到 B+Tree 中。 I think it reduces to this problem。我读过一些关于覆盖索引的文章,但也不认为它适用于此?
也许我只是没有插入足够多的行,这真的会给 postgres 带来麻烦?
【问题讨论】:
-
在我看来,
start和end的值是倒退的。你想用这个查询来实现什么?添加为问题的更新。 -
过滤条件的主要问题是两个谓词(
start < '2021-09-24 01:00:00.000'和end > '2018-09-24 01:00:00.000')都没有足够的选择性。换句话说,它们中的每一个都选择了大量的行。 PostgreSQL 决定它们都不适合索引扫描,而是执行堆扫描。现在,您可以为这些列中的任何一个设置下限和上限,甚至可以人为地设置吗?如果是这种情况,那么您可以推广使用索引。 -
我刚刚注意到。您正在从 100 万行中选择 790k 行。在这种情况下(选择了 79% 的行),堆扫描比索引扫描更有效。
-
您是否尝试过使用
tztsrange列并添加GiST 索引,然后使用intersection之类的范围运算符/函数? -
更新查询,感谢收看! @TheImpaler
标签: sql postgresql database-design