Postgres 不为日期字段使用索引
Postgres not using index for date field
我创建了类似
的索引
CREATE INDEX bill_open_date_idx ON bill USING btree(date(open_date));
并且,
Column | Type
open_date | timestamp without time zone
并说明分析如下
案例 1
explain analyze select * from bill where open_date >=date('2018-01-01');
Seq Scan on bill (cost=0.00..345264.60 rows=24813 width=1132) (actual time=0.007..1305.730 rows=5908 loops=1)
Filter: (open_date >= '2018-01-01'::date)
Rows Removed by Filter: 3238812
Total runtime: 1306.176 ms
案例 2
explain analyze select * from bill where open_date>='2018-01-01';
Seq Scan on bill (cost=0.00..345264.60 rows=24813 width=1132) (actual time=0.006..1220.697 rows=5908 loops=1)
Filter: (open_date>= '2018-01-01 00:00:00'::timestamp without time zone)
Rows Removed by Filter: 3238812
Total runtime: 1221.131 ms
案例 3
explain analyze select * from bill where date(open_date) >='2018-01-01';
Index Scan using idx_bill_open_date on bill (cost=0.43..11063.18 rows=22747 width=1132) (actual time=0.016..4.744 rows=5908 loops=1)
Index Cond: (date(open_date) >= '2018-01-01'::date)
Total runtime: 5.236 ms
(3 rows)
我对发生这种情况的原因做了足够的研究,但没有任何适当的解释。只有 case 3 使用了我创建的索引,其他的没有。为什么会这样?
据我了解,案例 2 搜索与列 open_date
等效的字符串,因此它没有使用索引。但是为什么不是case 1呢,还有说错的请指正
提前致谢!
编辑 1:另外,我很高兴知道发生了什么。
以下是要点的摘录 (https://gist.github.com/cobusc/5875282)
It is strange though that PostgreSQL rewrites the function used to
create the index to a canonical form, but does not seem to do the same
when the function is used in the WHERE clause (in order to match the
index function).
仍然,我不清楚为什么 postgres 的开发人员没有想到获取任何附近的匹配索引(或者我的索引是否无用,直到我明确地转换为 date
,如 case 3 )。考虑到 Postgres 是高度进化和可扩展的。
b-tree 索引只能用于搜索条件,如果条件如下所示:
<indexed expression> <operator> <expression that is constant during the index scan>
<indexed expression>
必须是您在 CREATE INDEX
语句中使用的表达式。
<operator>
必须属于数据类型和索引访问方法的默认运算符class,或者属于[=中指定的运算符class 13=].
<expression that is constant during the index scan>
可以是常数,也可以包含IMMUTABLE
或STABLE
函数和运算符,但不能包含VOLATILE
.
您的所有查询都满足后两个条件,但只有第三个满足第一个。这就是为什么只有那个查询可以使用索引。
有关详细介绍此内容的文档,请参阅 postgresql/src/backend/optimizer/path/indxpath.c
中对 match_clause_to_indexcol
的评论:
/*
* match_clause_to_indexcol()
* Determine whether a restriction clause matches a column of an index,
* and if so, build an IndexClause node describing the details.
*
* To match an index normally, an operator clause:
*
* (1) must be in the form (indexkey op const) or (const op indexkey);
* and
* (2) must contain an operator which is in the index's operator family
* for this column; and
* (3) must match the collation of the index, if collation is relevant.
*
* Our definition of "const" is exceedingly liberal: we allow anything that
* doesn't involve a volatile function or a Var of the index's relation.
* In particular, Vars belonging to other relations of the query are
* accepted here, since a clause of that form can be used in a
* parameterized indexscan. It's the responsibility of higher code levels
* to manage restriction and join clauses appropriately.
*
* Note: we do need to check for Vars of the index's relation on the
* "const" side of the clause, since clauses like (a.f1 OP (b.f2 OP a.f3))
* are not processable by a parameterized indexscan on a.f1, whereas
* something like (a.f1 OP (b.f2 OP c.f3)) is.
*
* Presently, the executor can only deal with indexquals that have the
* indexkey on the left, so we can only use clauses that have the indexkey
* on the right if we can commute the clause to put the key on the left.
* We handle that by generating an IndexClause with the correctly-commuted
* opclause as a derived indexqual.
*
* If the index has a collation, the clause must have the same collation.
* For collation-less indexes, we assume it doesn't matter; this is
* necessary for cases like "hstore ? text", wherein hstore's operators
* don't care about collation but the clause will get marked with a
* collation anyway because of the text argument. (This logic is
* embodied in the macro IndexCollMatchesExprColl.)
*
* It is also possible to match RowCompareExpr clauses to indexes (but
* currently, only btree indexes handle this).
*
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
* the clause is of the form "indexkey op ANY (arrayconst)".
*
* For boolean indexes, it is also possible to match the clause directly
* to the indexkey; or perhaps the clause is (NOT indexkey).
*
* And, last but not least, some operators and functions can be processed
* to derive (typically lossy) indexquals from a clause that isn't in
* itself indexable. If we see that any operand of an OpExpr or FuncExpr
* matches the index key, and the function has a planner support function
* attached to it, we'll invoke the support function to see if such an
* indexqual can be built.
我创建了类似
的索引CREATE INDEX bill_open_date_idx ON bill USING btree(date(open_date));
并且,
Column | Type
open_date | timestamp without time zone
并说明分析如下
案例 1
explain analyze select * from bill where open_date >=date('2018-01-01');
Seq Scan on bill (cost=0.00..345264.60 rows=24813 width=1132) (actual time=0.007..1305.730 rows=5908 loops=1)
Filter: (open_date >= '2018-01-01'::date)
Rows Removed by Filter: 3238812
Total runtime: 1306.176 ms
案例 2
explain analyze select * from bill where open_date>='2018-01-01';
Seq Scan on bill (cost=0.00..345264.60 rows=24813 width=1132) (actual time=0.006..1220.697 rows=5908 loops=1)
Filter: (open_date>= '2018-01-01 00:00:00'::timestamp without time zone)
Rows Removed by Filter: 3238812
Total runtime: 1221.131 ms
案例 3
explain analyze select * from bill where date(open_date) >='2018-01-01';
Index Scan using idx_bill_open_date on bill (cost=0.43..11063.18 rows=22747 width=1132) (actual time=0.016..4.744 rows=5908 loops=1)
Index Cond: (date(open_date) >= '2018-01-01'::date)
Total runtime: 5.236 ms
(3 rows)
我对发生这种情况的原因做了足够的研究,但没有任何适当的解释。只有 case 3 使用了我创建的索引,其他的没有。为什么会这样?
据我了解,案例 2 搜索与列 open_date
等效的字符串,因此它没有使用索引。但是为什么不是case 1呢,还有说错的请指正
提前致谢!
编辑 1:另外,我很高兴知道发生了什么。
以下是要点的摘录 (https://gist.github.com/cobusc/5875282)
It is strange though that PostgreSQL rewrites the function used to create the index to a canonical form, but does not seem to do the same when the function is used in the WHERE clause (in order to match the index function).
仍然,我不清楚为什么 postgres 的开发人员没有想到获取任何附近的匹配索引(或者我的索引是否无用,直到我明确地转换为 date
,如 case 3 )。考虑到 Postgres 是高度进化和可扩展的。
b-tree 索引只能用于搜索条件,如果条件如下所示:
<indexed expression> <operator> <expression that is constant during the index scan>
<indexed expression>
必须是您在CREATE INDEX
语句中使用的表达式。<operator>
必须属于数据类型和索引访问方法的默认运算符class,或者属于[=中指定的运算符class 13=].<expression that is constant during the index scan>
可以是常数,也可以包含IMMUTABLE
或STABLE
函数和运算符,但不能包含VOLATILE
.
您的所有查询都满足后两个条件,但只有第三个满足第一个。这就是为什么只有那个查询可以使用索引。
有关详细介绍此内容的文档,请参阅 postgresql/src/backend/optimizer/path/indxpath.c
中对 match_clause_to_indexcol
的评论:
/*
* match_clause_to_indexcol()
* Determine whether a restriction clause matches a column of an index,
* and if so, build an IndexClause node describing the details.
*
* To match an index normally, an operator clause:
*
* (1) must be in the form (indexkey op const) or (const op indexkey);
* and
* (2) must contain an operator which is in the index's operator family
* for this column; and
* (3) must match the collation of the index, if collation is relevant.
*
* Our definition of "const" is exceedingly liberal: we allow anything that
* doesn't involve a volatile function or a Var of the index's relation.
* In particular, Vars belonging to other relations of the query are
* accepted here, since a clause of that form can be used in a
* parameterized indexscan. It's the responsibility of higher code levels
* to manage restriction and join clauses appropriately.
*
* Note: we do need to check for Vars of the index's relation on the
* "const" side of the clause, since clauses like (a.f1 OP (b.f2 OP a.f3))
* are not processable by a parameterized indexscan on a.f1, whereas
* something like (a.f1 OP (b.f2 OP c.f3)) is.
*
* Presently, the executor can only deal with indexquals that have the
* indexkey on the left, so we can only use clauses that have the indexkey
* on the right if we can commute the clause to put the key on the left.
* We handle that by generating an IndexClause with the correctly-commuted
* opclause as a derived indexqual.
*
* If the index has a collation, the clause must have the same collation.
* For collation-less indexes, we assume it doesn't matter; this is
* necessary for cases like "hstore ? text", wherein hstore's operators
* don't care about collation but the clause will get marked with a
* collation anyway because of the text argument. (This logic is
* embodied in the macro IndexCollMatchesExprColl.)
*
* It is also possible to match RowCompareExpr clauses to indexes (but
* currently, only btree indexes handle this).
*
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
* the clause is of the form "indexkey op ANY (arrayconst)".
*
* For boolean indexes, it is also possible to match the clause directly
* to the indexkey; or perhaps the clause is (NOT indexkey).
*
* And, last but not least, some operators and functions can be processed
* to derive (typically lossy) indexquals from a clause that isn't in
* itself indexable. If we see that any operand of an OpExpr or FuncExpr
* matches the index key, and the function has a planner support function
* attached to it, we'll invoke the support function to see if such an
* indexqual can be built.