【问题标题】:How to optimize an intersection date range query in postgres如何优化 postgres 中的交叉日期范围查询
【发布时间】: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 带来麻烦?

【问题讨论】:

  • 在我看来,startend 的值是倒退的。你想用这个查询来实现什么?添加为问题的更新。
  • 过滤条件的主要问题是两个谓词(start &lt; '2021-09-24 01:00:00.000'end &gt; '2018-09-24 01:00:00.000')都没有足够的选择性。换句话说,它们中的每一个都选择了大量的行。 PostgreSQL 决定它们都不适合索引扫描,而是执行堆扫描。现在,您可以为这些列中的任何一个设置下限和上限,甚至可以人为地设置吗?如果是这种情况,那么您可以推广使用索引。
  • 我刚刚注意到。您正在从 100 万行中选择 790k 行。在这种情况下(选择了 79% 的行),堆扫描比索引扫描更有效。
  • 您是否尝试过使用tztsrange 列并添加GiST 索引,然后使用intersection 之类的范围运算符/函数?
  • 更新查询,感谢收看! @TheImpaler

标签: sql postgresql database-design


【解决方案1】:

PostgreSQL 支持范围数据类型 - 包括时间范围,例如TSTZRANGE。范围类型支持 GiST 索引 (https://www.postgresql.org/docs/current/btree-gist.html),通常用作 exclusion constraints 以防止时间范围重叠,例如为您的牙医预订时段时。

由于您的查询似乎是 Give me all timeframes which DO NOT overlap with the given timeframe 类型 - 范围类型和 Btree_GiST 索引看起来是一个完美的解决方案。

因此,startend 将改为一列 [start, end),而不是两列 - 这意味着时间间隔在左侧关闭并在右侧打开(start 包含在时间范围内,但end 被排除在外)。

【讨论】:

  • 您还可以在使用两个时间戳列时创建索引,通过索引创建 tsrange 的表达式
猜你喜欢
  • 2014-09-29
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 2021-06-01
相关资源
最近更新 更多