我有一个简单的表格,其中有一个名为user_birthday的日期字段(可以是NULL值)
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)
)
该字段有一个索引(B树),规则为NOT user_birthday IS 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"
正如您所看到的,
<@ daterange
没有使用现有索引,而BETWEEN
则使用了。
需要注意的是,这个规则的实际用例在一个更复杂的查询中,不会导致重新检查条件和位图堆扫描。在应用程序复杂查询中,两种方法(有120万条记录)之间的差异是巨大的:查询1为415毫秒,查询2为84毫秒。
这是daterange的错误吗?我做错了什么吗?还是datarange <@
的性能符合设计?
还有一个pgsql-bugs邮件列表的讨论。
analyze users;
,然后执行查询#1,执行计划会发生什么? - Mike Sherrill 'Cat Recall'