まあ、少なくともインデックスが使用されます。ただし、通常のインデックススキャンの代わりにビットマップインデックススキャンを取得します。これは、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ミリ秒かかります
- hstoreから1つのキー/値を抽出すると、約0.12 µs/行というわずかな追加コストが追加されます
- xpathを使用してxmlから1つのキー/値を抽出すると、約33 µs/行という莫大なコストが追加されます
結論:
- xmlは遅いです(しかし、誰もがそれを知っています)
- 柔軟なキー/値ストアを列に配置する場合は、hstoreを使用します
また、xmlデータはかなり大きいため、トーストされます(圧縮されてメインテーブルから保存されます)。これにより、メインテーブルの行がはるかに小さくなり、ページあたりの行数が増え、ページ上のすべての行を再チェックする必要があるため、ビットマップスキャンの効率が低下します。
ただし、これは修正できます。何らかの理由で、xpath()関数(xmlを処理するため、非常に低速です)のコストは、たとえば整数演算子「+」と同じ(1単位)です...
update pg_proc set procost=1000 where proname='xpath';
コスト値を微調整する必要があるかもしれません。適切な情報が与えられると、プランナーはxpathが遅いことを認識し、代わりにインデックススキャンを使用して、ビットマップインデックススキャンを回避します。これにより、ページ上のすべての行の条件を再確認する必要がなくなります。
これは行推定の問題を解決しないことに注意してください。 xml(またはhstore)の内部を分析できないため、行数のデフォルトの見積もり(ここでは500)を取得します。したがって、プランナーは完全に間違っている可能性があり、一部の結合が関係している場合は壊滅的な計画を選択します。これに対する唯一の解決策は、適切な列を使用することです。