Cóż, przynajmniej używa się indeksu. Otrzymasz jednak skanowanie indeksu bitmapy zamiast normalnego skanowania indeksu, co oznacza, że funkcja xpath() będzie wywoływana wiele razy.
Zróbmy mały test :
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
Jak widać,
- skanowanie całej tabeli z count(*) zajmuje 25 ms
- pobranie jednego klucza/wartości z hstore powoduje niewielki dodatkowy koszt, około 0,12 µs/wiersz
- wyodrębnienie jednego klucza/wartości z pliku xml za pomocą xpath dodaje ogromny koszt, około 33 µs/wiersz
Wnioski :
- xml działa wolno (ale wszyscy o tym wiedzą)
- jeśli chcesz umieścić elastyczny magazyn klucz/wartość w kolumnie, użyj hstore
Ponadto, ponieważ twoje dane xml są dość duże, zostaną one upieczone (skompresowane i zapisane poza główną tabelą). To sprawia, że wiersze w głównej tabeli są znacznie mniejsze, stąd więcej wierszy na stronę, co zmniejsza wydajność skanowania bitmap, ponieważ wszystkie wiersze na stronie muszą zostać ponownie sprawdzone.
Możesz to jednak naprawić. Z jakiegoś powodu funkcja xpath() (która jest bardzo powolna, ponieważ obsługuje xml) ma taki sam koszt (1 jednostka) jak powiedzmy operator liczb całkowitych "+"...
update pg_proc set procost=1000 where proname='xpath';
Może być konieczne dostosowanie wartości kosztu. Po otrzymaniu właściwych informacji planista wie, że xpath jest powolny i uniknie skanowania indeksu bitmapy, używając zamiast tego skanowania indeksu, które nie wymaga ponownego sprawdzania warunku dla wszystkich wierszy na stronie.
Zwróć uwagę, że to nie rozwiązuje problemu szacowania wierszy. Ponieważ nie możesz ANALIZOWAĆ wnętrza xml (lub hstore), otrzymujesz domyślne szacunki liczby wierszy (tutaj 500). Tak więc planista może się całkowicie mylić i wybrać plan katastroficzny, jeśli w grę wchodzą niektóre połączenia. Jedynym rozwiązaniem tego problemu jest użycie odpowiednich kolumn.