Postgresql postgres 9.6索引仅扫描逻辑上可能但未执行的功能索引

Postgresql postgres 9.6索引仅扫描逻辑上可能但未执行的功能索引,postgresql,Postgresql,我在Postgres发布的docs/wiki中读到了功能索引和仅索引扫描 我现在有一个类似的问题: SELECT(xpath('/document/uuid/text()', xmldata))[1]::text, (xpath('/document/title/text()', xmldata))[1]::text FROM xmltable WHERE(xpath('/document/uuid/text()', xmldata))[1]::text = 'some-uuid

我在Postgres发布的docs/wiki中读到了功能索引和仅索引扫描

我现在有一个类似的问题:

SELECT(xpath('/document/uuid/text()', xmldata))[1]::text,  
      (xpath('/document/title/text()', xmldata))[1]::text
FROM xmltable
WHERE(xpath('/document/uuid/text()', xmldata))[1]::text = 'some-uuid-xxxx-xxxx'
和一个索引:

CREATE INDEX idx_covering_index on xmltable using btree (
    ((xpath('/document/uuid/text()', xmldata))[1]::text),     
    ((xpath('/document/title/text()', xmldata))[1]::text)
)
从逻辑上看,该索引是一个覆盖索引,应该启用仅索引扫描,因为所有查询的值都包含在索引中(uuid和title)

我现在碰巧知道,如果函数调用中使用的列也包含在内,Postgres只能识别函数索引上的覆盖索引

例如:

1) 此索引无法涵盖:

CREATE INDEX idx_covering_index on xmltable using btree (id, to_upper(column1));
2) 但这一条可以涵盖:

CREATE INDEX idx_covering_index on xmltable using btree (column1, id, to_upper(column1));
从而导致只进行索引扫描

如果我现在在xml设置中尝试此操作:

CREATE INDEX idx_covering_index on xmltable using btree (xmldata,
    ((xpath('/document/uuid/text()', xmldata))[1]::text),     
    ((xpath('/document/title/text()', xmldata))[1]::text)
)
我得到一个错误:

数据类型xml没有访问方法“btree”的默认运算符类

公平地说,不幸的是,通常使用的
“text\u ops”
“text\u pattern\u ops”
不接受“xml”作为输入-因此呈现我的索引-尽管它将覆盖所有值-无法支持仅索引扫描

是否可以通过提供仅索引扫描的可能性来处理此问题

@编辑1:

我知道postgres不能使用1)中的索引作为覆盖索引,但可以使用2)这样的索引

我还尝试用非常简单的表格来验证这种行为,我还记得读过这篇文章——但我一辈子都记不起在哪里

create table test (
    id serial primary key,
    quote text
)



insert into test (number, quote) values ('I do not know any clever quotes');
insert into test (number, quote) values ('I am sorry');



CREATE INDEX idx_test_functional on test using btree ((regexp_replace(quote, '^I ', 'BillDoor ')));
set enable_seqscan = off;

analyze test;

explain select quote from test where regexp_replace(quote, '^I ', 'BillDoor ') = 'BillDoor do not know any clever quotes'

--> "Index Scan using idx_test_functional on test  (cost=0.13..8.15 rows=1 width=27)"

drop index idx_test_functional;
CREATE INDEX idx_test_functional on test using btree (quote, (regexp_replace(quote, '^I ', 'BillDoor ')));

analyze test;

explain select quote from test where regexp_replace(quote, '^I ', 'BillDoor ') = 'BillDoor do not know any clever quotes'

--> "Index Only Scan using idx_test_functional on test  (cost=0.13..12.17 rows=1 width=27)"
@编辑2:

xmltable的完整表定义:

id serial primary key (clustered),
xmldata xml (only data used to filter queries)
history xml (never queried or read, just kept in case of legal inquiry)
fileinfo text (seldom quieried, sometimes retrieved)
"timestamp" timestamp (mainly for legal inquiries too)
该表包含约500.000条记录,xmldata的大小在350到800字节之间,历史记录要大得多,但很少检索,也从未在筛选器中使用过

为了确保得到真正的结果,我总是在创建或删除索引后运行
analyze-xmltable

查询的完整执行计划:

explain analyze select (xpath('/document/uuid/text()', d.xmldata))[1]::text as uuid
from xmltable as d
where
(xpath('/document/uuid/text()', d.xmldata))[1]::text = 'some-uuid-xxxx-xxxx' and (xpath('/document/genre/text()', d.xmldata))[1]::text = 'bio'
这些独立性包括:

create index idx_genre on xmltable using btree (((xpath('/document/genre/text()', xmldata))[1]::text));

create index idx_uuid on xmltable using btree (((xpath('/document/uuid/text()', xmldata))[1]::text)); 

create index idx_uuid_genre on xmltable using btree (((xpath('/document/uuid/text()', xmldata))[1]::text), ((xpath('/document/genre/text()', xmldata))[1]::text));
首先导致:

"Index Scan using idx_genre on xmldata d  (cost=0.42..6303.05 rows=18154 width=32)"
"  Index Cond: (((xpath('/document/genre/text()'::text, xmldata, '{}'::text[]))[1])::text = 'bio'::text)"
"  Filter: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)"
很公平,我想,只是为了测试,我会强迫它使用我心目中的覆盖指数:

drop index idx_uuid;
drop index idx_genre;
现在我得到:

"Bitmap Heap Scan on xmltable d  (cost=551.13..16025.51 rows=18216 width=32)"
"  Recheck Cond: ((((xpath('/document/genre/text()'::text, xmldata, '{}'::text[]))[1])::text = 'bio'::text) AND (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text))"
"  ->  Bitmap Index Scan on idx_uuid_genre  (cost=0.00..546.58 rows=18216 width=0)"
"        Index Cond: ((((xpath('/document/genre/text()'::text, xmldata, '{}'::text[]))[1])::text = 'bio'::text) AND (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text))"
我还尝试在索引中切换uuid和流派的位置,同样的执行计划。

编辑最终版本:为什么不可能

根据文档:当索引类型支持此功能时,postgresql可以进行索引扫描(即btree始终支持此功能,GiST和SpGiST仅适用于某些特定运算符,而GIN根本不支持)。并且可以从索引中重建原始索引值

第二个要求是最有趣的

对于列,它很简单
(a,b)
,您的索引能够重建原始存储值

对于使函数索引工作的函数,您应该创建具有原始值的索引。这意味着
(f1(a),f2(b))
索引将再次出现在表中,因为您无法从这些值重建索引数据
(a,b)
。开发人员建议的解决方法是创建索引
(f1(a)、f2(b)、a、b)
。在这种情况下,查询计划器可以确定是否可以运行仅索引扫描,因为索引包含原始数据

回到您的问题,在xml列上创建仅索引扫描是不可能的:并没有支持xml数据比较的操作符,这对btree至关重要。xml数据没有比较运算符的定义。因此,您不能在任何类型的索引中使用此列,但您需要在仅索引扫描中使用它来提示查询优化器执行仅索引扫描

编辑:(解决方案如何在特定xpath表达式上实现仅索引扫描)

如果您知道这些数据将被频繁使用,我建议通过触发器函数解决这个问题,并创建另外两个字段,并通过索引覆盖它们。诸如此类:

ALTER TABLE public.xmltable ADD COLUMN xpath_uuid character varying(36);
ALTER TABLE public.xmltable ADD COLUMN xpath_title character varying(100);


CREATE INDEX idx_covering_materialized_xml_data
  ON public.xmltable
  USING btree
  (xpath_uuid COLLATE pg_catalog."default", xpath_title COLLATE pg_catalog."default");

CREATE OR REPLACE FUNCTION public.introduce_xml_materialization()
  RETURNS trigger AS
$BODY$BEGIN 

NEW.xpath_uuid = (xpath('/document/uuid/text()', NEW.xmldata))[1]::text;
NEW.xpath_title = (xpath('/document/title/text()', NEW.xmldata))[1]::text;

RETURN NEW; 
END;$BODY$
  LANGUAGE plpgsql STABLE
  COST 100;



CREATE TRIGGER index_xml_data
  BEFORE INSERT OR UPDATE
  ON public.xmltable
  FOR EACH ROW
  EXECUTE PROCEDURE public.introduce_xml_materialization();
然后你可以简单地做:

SELECT  xpath_uuid, xpath_title
  FROM public.xmltable
  where xpath_uuid = ' uuid1 '
这将显示仅索引扫描:

"Index Only Scan using idx_covering_materialized_xml_data on xmltable  (cost=0.14..8.16 rows=1 width=308)"
"  Index Cond: (xpath_uuid = ' uuid1 '::text)"
假设数据读的比写的多,这种方法将是最佳的。从插入或更新的成本来看,它通常与在xpath表达式上创建函数索引相同

原始回答:(对于那些愿意调整查询优化器的人来说可能很有趣)

问题是您的查询优化器认为xPath函数调用是最简单的。i、 这就像调用简单的数学运算符,其成本为1。在这种情况下,查询优化器认为,从表中提取并再次计算,然后执行纯索引扫描更容易

如果将xpath调用成本增加到1000,查询优化器将发现这样的调用要困难得多(这实际上是事实),并将尝试执行仅索引扫描。在我的测试设置中,我执行了

update pg_proc set procost=1 where proname='xpath';  
执行计划是

"Bitmap Heap Scan on xmltable  (cost=4.17..11.30 rows=3 width=64)"
"  Recheck Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)"
"  ->  Bitmap Index Scan on idx_covering_index_3  (cost=0.00..4.17 rows=3 width=0)"
"        Index Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)"
但当我这么做的时候

update pg_proc set procost=1000 where proname='xpath';
执行计划正在切换到仅索引扫描

"Index Scan using idx_covering_index_3 on xmltable  (cost=0.15..31.20 rows=3 width=64)"
"  Index Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)"
在我的卷(即没有数据)上,仅索引查询的最小成本明显小于原始索引+表扫描,但最大成本更大。因此,如果您在查询优化上作弊,可能需要在xpath调用成本上设置更高的值


希望这会有所帮助,出于好奇,让我们看看使用纯索引查询的好处

我现在碰巧知道,如果函数调用中使用的列也包含在函数索引中,postgres只会识别函数索引中的覆盖索引
你能提供任何支持这一点的文档参考吗;这很违反直觉。那就是。。。您如何“现在知道”。表
xmltable
的完整定义是什么?如果存在索引,语句的执行计划是什么?Postgres是否使用索引进行索引查找,还是根本不使用该索引?我知道,因为我尝试过使用或不使用xml、常规表、json等。这对我来说也很奇怪-我将编辑问题以附加此内容。我似乎记得这与重新检查有关。在您的示例中,查询计划没有切换到索引
"Index Scan using idx_covering_index_3 on xmltable  (cost=0.15..31.20 rows=3 width=64)"
"  Index Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)"