Я прочитал о функциональных индексах и индексах только в документах/вики, опубликованных Postgres.
Теперь у меня есть запрос вроде:
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 распознает только индексы покрытия по функциональным индексам, если столбцы, используемые в вызовах функций, также содержатся
например:.
SELECT to_upper(column1) from table where id >10
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_ops"
или "text_pattern_ops"
не принимают "xml" в качестве входных данных - таким образом, отображая мой индекс - хотя он будет охватывать все значения - неспособно поддерживать только индексы.
Можно ли это обрабатывать таким образом, чтобы обеспечить возможность сканирования только по индексу?
@EDIT1:
Я знаю, что 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)"
@EDIT2:
Полное определение таблицы 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 и жанра в индекс, тот же план выполнения.