【问题标题】:Postgres not using index for date fieldPostgres 不使用日期字段的索引
【发布时间】:2019-11-05 23:11:36
【问题描述】:

我已经创建了类似的索引

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 正在使用我创建的索引,而不是其他索引。为什么会这样?

据我了解,case 2 搜索与列 open_date 等效的字符串,因此它不使用索引。但是为什么不是案例1。另外,如果我错了,请纠正我。

提前致谢!

编辑 1:另外,我很高兴能深入了解正在发生的事情。

以下是要点摘录 (https://gist.github.com/cobusc/5875282)

奇怪的是 PostgreSQL 重写了以前的函数 将索引创建为规范形式,但似乎没有做同样的事情 当函数在 WHERE 子句中使用时(为了匹配 索引函数)。

不过,我不清楚为什么 postgres 的开发人员没有考虑获取任何附近的匹配索引(或者我的索引在我明确地转换为 date 之前是无用的,如 case 3)。考虑到 Postgres 是高度进化和可扩展的。

【问题讨论】:

  • 向问题添加执行计划时,请确保保留其格式(主要是缩进),因为这对于正确阅读计划至关重要。
  • 当然!对不起! :)

标签: postgresql b-tree-index


【解决方案1】:

如果条件如下所示,则只能将 b-tree 索引用于搜索条件:

<indexed expression> <operator> <expression that is constant during the index scan>
  • &lt;indexed expression&gt; 必须是您在 CREATE INDEX 语句中使用的表达式。

  • &lt;operator&gt; 必须属于数据类型和索引访问方法的默认运算符类,或者属于CREATE INDEX 中指定的运算符类。

  • &lt;expression that is constant during the index scan&gt; 可以是常量,也可以包含 IMMUTABLESTABLE 函数和运算符,但不包含 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 语句中使用的表达式。 @Laurenz:这个陈述很清楚,回答了这个问题。但是,为什么不进行任何增强来找到最接近的匹配索引并可能使用它呢?他们必须有一些强有力的理由来禁止它(至少,直到现在)。如果你知道为什么?你可以帮帮我!
  • 请记住,索引是排序列表,使用的排序是相关的。不能保证date(open_date) 的排序方式与open_date 相同。所以date(open_date) &gt;= '2018-01-01'::date 并不意味着open_date &gt;=date('2018-01-01')。因此,您从原始索引上的索引扫描获得的结果不需要是该条件的正确结果。如果您碰巧始终使用 ISO 日期,它会“偶然”起作用,但 PostgreSQL 不知道这一点。
  • 很好!!谢谢!! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-26
  • 1970-01-01
  • 2011-02-20
  • 2011-12-11
相关资源
最近更新 更多