Е, поне индексът се използва. Все пак получавате сканиране на растерни индекси вместо нормално сканиране на индекси, което означава, че функцията xpath() ще бъде извиквана много пъти.
Нека направим малка проверка:
CREATE TABLE foo ( id serial primary key, x xml, h hstore );
insert into foo (x,h) select XMLPARSE( CONTENT '<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<object_id>2</object_id>
<pack_form_id>' || n || '</pack_form_id>
<prod_form_id>34</prod_form_id>
</row>' ),
('object_id=>2,prod_form_id=>34,pack_form_id=>'||n)::hstore
FROM generate_series( 1,100000 ) n;
test=> EXPLAIN ANALYZE SELECT count(*) FROM foo;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Aggregate (cost=4821.00..4821.01 rows=1 width=0) (actual time=24.694..24.694 rows=1 loops=1)
-> Seq Scan on foo (cost=0.00..4571.00 rows=100000 width=0) (actual time=0.006..13.996 rows=100000 loops=1)
Total runtime: 24.730 ms
test=> explain analyze select * from foo where (h->'pack_form_id')='123';
QUERY PLAN
----------------------------------------------------------------------------------------------------
Seq Scan on foo (cost=0.00..5571.00 rows=500 width=68) (actual time=0.075..48.763 rows=1 loops=1)
Filter: ((h -> 'pack_form_id'::text) = '123'::text)
Total runtime: 36.808 ms
test=> explain analyze select * from foo where ((xpath('//pack_form_id/text()'::text, x))[1]::text) = '123';
QUERY PLAN
------------------------------------------------------------------------------------------------------
Seq Scan on foo (cost=0.00..5071.00 rows=500 width=68) (actual time=4.271..3368.838 rows=1 loops=1)
Filter: (((xpath('//pack_form_id/text()'::text, x, '{}'::text[]))[1])::text = '123'::text)
Total runtime: 3368.865 ms
Както виждаме,
- сканирането на цялата таблица с count(*) отнема 25 ms
- извличането на един ключ/стойност от hstore добавя малка допълнителна цена, около 0,12 µs/ред
- извличането на един ключ/стойност от xml чрез xpath добавя огромни разходи, около 33 µs/ред
Заключения :
- xml е бавен (но всеки знае това)
- ако искате да поставите гъвкаво хранилище за ключ/стойност в колона, използвайте hstore
Освен това, тъй като вашите xml данни са доста големи, те ще бъдат препечени (компресирани и съхранени извън основната таблица). Това прави редовете в главната таблица много по-малки, следователно повече редове на страница, което намалява ефективността на растерните сканирания, тъй като всички редове на страницата трябва да бъдат проверени отново.
Можете обаче да поправите това. По някаква причина функцията xpath() (която е много бавна, тъй като обработва xml) има същата цена (1 единица) като, да кажем, операторът за цяло число "+"...
update pg_proc set procost=1000 where proname='xpath';
Може да се наложи да промените стойността на разходите. Когато получи правилната информация, плановият знае, че xpath е бавен и ще избегне сканиране на индекс на растерна графика, като вместо това използва сканиране на индекс, което не изисква повторна проверка на условието за всички редове на страница.
Обърнете внимание, че това не решава проблема с оценките на редовете. Тъй като не можете да АНАЛИЗИРАТЕ вътрешността на xml (или hstore), получавате оценки по подразбиране за броя на редовете (тук 500). Така че планиращият може да греши напълно и да избере катастрофален план, ако са включени някои присъединявания. Единственото решение за това е да използвате правилни колони.