St.*_*rio 5 sql postgresql indexing internals
PostgreSQL 9.4该表创建如下:
CREATE TABLE foo (
id integer,
date date,
value numeric(14,3)
);
Run Code Online (Sandbox Code Playgroud)
我正在使用ROW_NUMBER()窗口函数优化查询COALESCE.为了最有效,我倾向于Index Only Scan在以下查询中使用:
SELECT id, c_val
FROM (
SELECT id, COALESCE(value, 0) c_val, ROW_NUMBER() OVER(PARTITION BY id ORDER BY date DESC NULLS LAST) rn
FROM foo) sbt
WHERE sbt.rn = 1;
Run Code Online (Sandbox Code Playgroud)
所以,如果我创建索引如下:
CREATE INDEX ON foo (id, date DESC NULLS LAST, value);
Run Code Online (Sandbox Code Playgroud)
规划师选择使用Index Only Scan,但如果我这样做:
CREATE INDEX ON foo (id, date DESC NULLS LAST, COALESCE(value, 0));
Run Code Online (Sandbox Code Playgroud)
规划师会做的Index Scan.
为什么?我试图避免COALESCE在执行查询时评估函数的成本.为什么不适用Index Only Scan?
我认为您COALESCE(value, 0)在SELECT索引使用方面错误地假设了这一点。说实话,这只是在返回行值之后完成的视图转换。
就索引使用而言,重要的是您的WINDOW FUNCTION. 首先按 进行分区id,然后按 来对每个分区中的值进行排序date DESC NULLS LAST。这两件事决定了CREATE INDEX ON foo (id, date DESC NULLS LAST, ...)无论您将其放在下一个位置,索引都是有用的。请注意,如果您在创建索引时更改和 的顺序,PostgreSQL 将根本不会使用该索引。iddate
现在,您必须知道,INDEX ONLY SCAN仅当索引本身存储查询请求的整个未更改行值时才可以使用。PostgreSQL 手册之后:
如果索引存储原始索引数据值(而不是它们的一些有损表示),则支持仅索引扫描很有用,其中索引返回实际数据......
在您的情况下,您的第二个索引存储行的一些有损表示,因为最后一列的值是由函数和查询要求的id,value和转换的date。PostgreSQL 并没有聪明到它只是NULLsby的替代0。对他来说这不是原来的价值。所以我们需要访问表来获取原始行值(最后使用 plain INDEX SCAN)。之后,值被格式化以供输出并COALESCE(values, 0)发生。
编辑:
我认为就您有关内部的问题而言,这个解释对您来说已经足够了。要谈论COALECE()评估成本,我同意a_horse_with_no_name 的观点,您可能不应该担心这一点。
| 归档时间: |
|
| 查看次数: |
214 次 |
| 最近记录: |