Yah, setidaknya indeks digunakan. Anda mendapatkan pemindaian indeks bitmap alih-alih pemindaian indeks normal, yang berarti fungsi xpath() akan dipanggil berkali-kali.
Mari kita lakukan sedikit pemeriksaan :
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
Seperti yang kita lihat,
- memindai seluruh tabel dengan hitungan(*) membutuhkan waktu 25 md
- mengekstrak satu kunci/nilai dari hstore menambahkan sedikit biaya tambahan, sekitar 0,12 s/baris
- mengekstrak satu kunci/nilai dari xml menggunakan xpath menambah biaya yang sangat besar, sekitar 33 s/baris
Kesimpulan :
- xml lambat (tapi semua orang tahu itu)
- jika Anda ingin meletakkan penyimpanan kunci/nilai fleksibel di kolom, gunakan hstore
Juga karena data xml Anda cukup besar, itu akan dipanggang (dikompresi dan disimpan dari tabel utama). Hal ini membuat baris dalam tabel utama jauh lebih kecil, sehingga lebih banyak baris per halaman, yang mengurangi efisiensi pemindaian bitmap karena semua baris pada halaman harus diperiksa ulang.
Anda dapat memperbaiki ini sekalipun. Untuk beberapa alasan fungsi xpath() (yang sangat lambat, karena menangani xml) memiliki biaya yang sama (1 unit) seperti yang dikatakan, operator bilangan bulat "+"...
update pg_proc set procost=1000 where proname='xpath';
Anda mungkin perlu mengubah nilai biaya. Saat diberi info yang benar, perencana mengetahui bahwa xpath lambat dan akan menghindari pemindaian indeks bitmap, menggunakan pemindaian indeks, yang tidak perlu memeriksa ulang kondisi untuk semua baris pada halaman.
Perhatikan bahwa ini tidak menyelesaikan masalah estimasi baris. Karena Anda tidak dapat MENGANALISIS bagian dalam xml (atau hstore), Anda mendapatkan perkiraan default untuk jumlah baris (di sini, 500). Jadi, perencana mungkin sepenuhnya salah dan memilih rencana bencana jika beberapa gabungan terlibat. Satu-satunya solusi untuk ini adalah dengan menggunakan kolom yang tepat.