【问题标题】:PostgreSQL daterange not using index correctlyPostgreSQL 日期范围未正确使用索引
【发布时间】:2014-05-14 11:53:29
【问题描述】:

我有一个简单的表,其中有一个 user_birthday 字段,类型为日期(可以是 空值)

CREATE TABLE users
(
  user_id bigserial NOT NULL,
  user_email text NOT NULL,
  user_password text,
  user_first_name text NOT NULL,
  user_middle_name text,
  user_last_name text NOT NULL,
  user_birthday date,
  CONSTRAINT pk_users PRIMARY KEY (user_id)
)

在该字段上定义了一个索引(btree),其规则为 NOT user_birthday 为 NULL。

CREATE INDEX ix_users_birthday
  ON users
  USING btree
  (user_birthday)
  WHERE NOT user_birthday IS NULL;

试图跟进另一个想法,我添加了扩展名btree_gist 并创建了以下索引:

CREATE INDEX ix_users_birthday_gist
  ON glances.users
  USING gist
  (user_birthday)
  WHERE NOT user_birthday IS NULL;

但它也没有任何影响,因为据我所知,它不用于范围检查。

PostgreSQL 版本为 9.3.4.0 (22) Postgres.app 并且问题也存在于 9.3.3.0 (21) Postgres.app

我对以下问题很感兴趣:

查询 #1:

EXPLAIN ANALYZE SELECT *
FROM users
WHERE user_birthday <@ daterange('[1978-07-15,1983-03-01)')

查询 #2:

EXPLAIN ANALYZE SELECT *
FROM users
WHERE user_birthday BETWEEN '1978-07-15'::date AND '1983-03-01'::date

乍一看两者应该有相同的执行计划,但对于某些 原因,结果如下:

查询 #1:

"Seq Scan on users  (cost=0.00..52314.25 rows=11101 width=241) (actual
time=0.014..478.983 rows=208886 loops=1)"
"  Filter: (user_birthday <@ '[1978-07-15,1983-03-01)'::daterange)"
"  Rows Removed by Filter: 901214"
"Total runtime: 489.584 ms"

查询 #2:

"Bitmap Heap Scan on users  (cost=4468.01..46060.53 rows=210301 width=241)
(actual time=57.104..489.785 rows=209019 loops=1)"
"  Recheck Cond: ((user_birthday >= '1978-07-15'::date) AND (user_birthday
<= '1983-03-01'::date))"
"  Rows Removed by Index Recheck: 611375"
"  ->  Bitmap Index Scan on ix_users_birthday  (cost=0.00..4415.44
rows=210301 width=0) (actual time=54.621..54.621 rows=209019 loops=1)"
"        Index Cond: ((user_birthday >= '1978-07-15'::date) AND
(user_birthday <= '1983-03-01'::date))"
"Total runtime: 500.983 ms"

如您所见,&lt;@ daterange 没有利用现有索引,而 BETWEEN 会。

需要注意的是,此规则的实际用例是在更复杂的查询中, 这不会导致重新检查条件和位图堆扫描。 在应用程序复杂查询中,两种方法(120 万条记录)之间的差异是巨大的: 在 415 毫秒时查询 #1 在 84 毫秒时查询 #2。

这是日期范围的错误吗? 难道我做错了什么?或datarange &lt;@ 是否按设计执行?

pgsql-bugs mailing list也有讨论

【问题讨论】:

  • 如果您运行analyze users;,然后执行查询#1,执行计划会发生什么?
  • 当我看到这个问题时,这是我尝试的第一件事。没有影响。
  • 索引NOT user_birthday IS NULL而不是日期本身的理由是什么?
  • 问题应该显示CREATE INDEX脚本。
  • 同意,如果您有范围类型并且需要检查上限/下限(不包括/包括,NULL)来构建语句,这有点笨拙。如果 Postgres 可以为我们做到这一点,那很好。

标签: postgresql indexing date-range b-tree-index gist-index


【解决方案1】:

BETWEENincludes上下边框。你的情况

WHERE user_birthday BETWEEN '1978-07-15'::date AND '1983-03-01'::date

匹配

WHERE user_birthday &lt;@ daterange('[1978-07-15,1983-03-01<b>]</b>')

我看到你提到了 btree 索引。为此,请使用简单的比较运算符。

详细manual page on which index is good for which operators

范围类型运算符&lt;@ or @&gt; would work with GiST indexes.
示例:
Perform this hours of operation query in PostgreSQL

【讨论】:

  • 已经试过了。 postgres 自动将 [] daterange 转换为 [) daterange。没有影响 - 仍然是 Seq Scan。
  • 当您在日期范围字段上有索引时使用 GIST。这里我只是使用一个日期范围来过滤。
  • @Sash:将&lt;@ 与范围类型的要点索引或功能索引结合起来。对于 btree 索引,使用简单的比较运算符。
  • 我没有范围类型。添加了测试表的创建。我正在尝试按照您的建议创建复杂的 gist 索引,但似乎为此,我需要创建一个索引,将用户生日作为范围的两个值。
  • 好吧,我宁愿建议对 btree 索引使用简单的比较运算符。 :) GiST 索引对于最近邻搜索可能是一个好主意,但您的示例使用简单的比较运算符和 btree 索引效果更好。
猜你喜欢
  • 2016-01-14
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多