如何使用xpath表达式在PostgreSQL的XML列上创建索引?

时间:2018-09-04 20:59:14

标签: xml postgresql xpath indexing aws-aurora

尝试在AuroraDB-PostgreSQL 9.6上使用xpath表达式的XML数据类型列上创建btree索引时,我遇到了此错误:

ERROR:  could not identify a comparison function for type xml
SQL state: 42883

这个2009年没有明确解决方案的线程是我发现的唯一关于讨论此错误消息的信息,它涉及为早期版本的PostgreSQL创建基于xpath的索引: https://www.postgresql-archive.org/Slow-select-times-on-select-with-xpath-td2074839.html

在我的情况下,我还需要指定名称空间,并且该线程中的原始发布者将xpath表达式的结果转换为text [],这对我来说也是错误的-但是为什么还要这样做?即使我有成千上万的行要经过,我也看不到PostgreSQL使用过我的索引。

因此,我尝试了一种更简单的情况,但错误仍然出现-请说明为什么可以这样做

CREATE TABLE test
(
    id integer NOT NULL,
    xml_data xml NOT NULL,
    CONSTRAINT test_pkey PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;



CREATE INDEX test_idx
    ON test USING btree 
    (xpath('/book/title', xml_data))

,结果消息是:

ERROR:  could not identify a comparison function for type xml
SQL state: 42883

数据库编码为UTF8。 排序规则和字符类型为en_US.UTF-8。

一些示例插入语句:

insert into source_data.test(id, xml_data) 
values(1, XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>1</chapter><chapter>2</chapter></book>'))

insert into source_data.test(id, xml_data) 
values(2, XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Apropos</title><chapter>1</chapter><chapter>2</chapter></book>'))

1 个答案:

答案 0 :(得分:4)

由于XML data type不提供任何comparison operators,因此您收到此错误,因此您无法基于xpath()的结果创建索引,因为它返回了an array of XML values

因此,创建索引时需要将XPath表达式转换为文本数组:

CREATE INDEX test_idx
ON test USING BTREE 
    (cast(xpath('/book/title', xml_data) as text[])) ;

然后在查询表时使用此索引:

EXPLAIN ANALYZE
SELECT * FROM test where
cast(xpath('/book/title', xml_data) as text[]) = '{<title>Apropos</title>}';

给予

                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
Index Scan using test_idx on test  (cost=0.13..8.15 rows=1 width=36) (actual time=0.034..0.038 rows=1 loops=1)
    Index Cond: ((xpath('/book/title'::text, xml_data, '{}'::text[]))::text[] = '{<title>Apropos</title>}'::text[])
Planning time: 0.168 ms
Execution time: 0.073 ms (4 rows)

使用text()时,其作用相同:

CREATE INDEX test_idx
ON test USING BTREE 
    (cast(xpath('/book/title/text()', xml_data) as text[])) ;

explain analyze select * from test where
cast(xpath('/book/title/text()', xml_data) as text[]) = '{Apropos}';

给予

                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Index Scan using test_idx on test  (cost=0.13..8.15 rows=1 width=36) (actual time=0.034..0.038 rows=1 loops=1)
   Index Cond: ((xpath('/book/title/text()'::text, xml_data, '{}'::text[]))::text[] = '{Apropos}'::text[])
 Planning time: 0.166 ms
 Execution time: 0.076 ms
(4 rows)

请注意,由于创建的测试表中只有4行,因此我通过以下命令强制使用了索引。

SET enable_seqscan TO off;