0

220 万レコードの「メタ」というテーブルがあります。このテーブルには、productId、キー、および値の 3 つの列が含まれています。キーと値は、製品のメタ説明を含む 2 つの列です。

次のクエリは 2.6 秒かかり、676 件の結果を返します (PostgreSQL 8.4.13、CentOS 6.4 64 ビット)。このクエリは、ユーザーが他の 2 つのフィルター (年とソース) で既にフィルター処理している特定のフィルター (サイズ) から、可能なすべてのメタ ディスクリプションを取得するために使用されます。

このトピックの配列ソリューションを試しましたが、悪化するだけでした: PostgreSQL IN operator with subquery poor performance

2 つのサブクエリはかなり高速 (75 ミリ秒と 178 ミリ秒) ですが、それらを組み合わせるとパフォーマンスの問題が発生します。クエリを書き直す方法はありますか?

これは現在のクエリです。

SELECT DISTINCT ON(value) key, value 
FROM   "meta" 
WHERE  key = 'size'
    AND "productId" IN (SELECT "productId" 
        FROM   "meta" 
        WHERE  "value" = 'ibm'
            AND "key" = 'source' )
    AND "productId" IN (SELECT "productId" 
        FROM  "meta"
        WHERE "value" >= '1920'
            AND "value" <= '2010' 
            AND "key" = 'year' ) 
ORDER  BY value 

次の EXPLAIN ANALYZE を使用します。

Unique  (cost=38829.46..38843.19 rows=564 width=15) (actual time=2674.474..2690.856 rows=676 loops=1)
  ->  Sort  (cost=38829.46..38836.32 rows=2745 width=15) (actual time=2674.471..2681.333 rows=66939 loops=1)
        Sort Key: public."meta".value
        Sort Method:  quicksort  Memory: 8302kB
        ->  Hash Join  (cost=32075.86..38672.69 rows=2745 width=15) (actual time=472.158..2472.002 rows=66939 loops=1)
              Hash Cond: (public."meta"."originalId" = public."meta"."productId")
              ->  Nested Loop  (cost=15079.41..21563.33 rows=13109 width=23) (actual time=113.873..1013.113 rows=104307 loops=1)
                    ->  HashAggregate  (cost=15079.41..15089.21 rows=980 width=4) (actual time=113.802..163.805 rows=105204 loops=1)
                          ->  Bitmap Heap Scan on "meta"  (cost=315.39..15051.42 rows=11196 width=4) (actual time=24.540..68.237 rows=105204 loops=1)
                                Recheck Cond: (((key)::text = 'source'::text) AND ((value)::text = 'KADASTER_WOII_RAF_USAAF'::text))
                                ->  Bitmap Index Scan on "productMetadataKeyValueIndex"  (cost=0.00..312.60 rows=11196 width=0) (actual time=23.506..23.506 rows=105204 loops=1)
                                      Index Cond: (((key)::text = 'source'::text) AND ((value)::text = 'ibm'::text))
                    ->  Index Scan using "idx_productId" on "meta"  (cost=0.00..6.59 rows=1 width=19) (actual time=0.006..0.008 rows=1 loops=105204)
                          Index Cond: (public."meta"."productId" = public."meta"."productId")
                          Filter: ((public."meta".key)::text = 'size'::text)
              ->  Hash  (cost=16954.58..16954.58 rows=3350 width=4) (actual time=358.214..358.214 rows=184571 loops=1)
                    ->  HashAggregate  (cost=16921.08..16954.58 rows=3350 width=4) (actual time=258.149..319.154 rows=184571 loops=1)
                          ->  Bitmap Heap Scan on "meta"  (cost=1172.62..16825.39 rows=38273 width=4) (actual time=86.725..167.110 rows=184571 loops=1)
                                Recheck Cond: (((key)::text = 'year'::text) AND ((value)::text >= '1920'::text) AND ((value)::text <= '2010'::text))
                                ->  Bitmap Index Scan on "productMetadataKeyIndex"  (cost=0.00..1163.05 rows=38273 width=0) (actual time=83.992..83.992 rows=184571 loops=1)
                                      Index Cond: (((key)::text = 'year'::text) AND ((value)::text >= '1920'::text) AND ((value)::text <= '2010'::text))
Total runtime: 2696.276 ms

定義されたインデックス:

idx_productId   CREATE INDEX "idx_productId" ON "meta" USING btree ("productId")    
productMetaUnique_id    CREATE UNIQUE INDEX "productMetaUnique_id" ON "meta" USING btree ("productId", key)     
productMetadataKeyIndex CREATE INDEX "productMetadataKeyIndex" ON "meta" USING btree (key)  
productMetadataKeyValueIndex    CREATE INDEX "productMetadataKeyValueIndex" ON "meta" USING btree (key, value)
4

1 に答える 1

0

まず、PostgreSQL 8.1.4 はアンティークです。それ以降のすべてのリリース (8.2、8.3、8.4、9.0、9.1、および 9.2) でクエリ プランナーが改善されているため、アップグレードしてください。

次に、結合とグループ化を使用するようにクエリを書き直して、より良い計画を立てることができます。

select m1."value"
from meta as m1
join meta as m2 on m2."productId" = m1."productId"
               and m2."key" = 'source'
               and m2."value" = 'ibm'
join meta as m3 on m3."productId" = m1."productId"
               and m3."key" = 'year'
               and m3."value" between 1920 and 2010
where m1."key" = 'size'
group by m1."value"

後者はおそらく、PG 9.2 のインデックス オンリー スキャンでインデックスを使用して(key, product_id)(product_id, key, value)テーブル ルックアップを完全に回避できます。

次に、製品テーブルにデータを直接配置する必要がある場合です。それに対してクエリを実行している場合、そもそもメタに属していない可能性があります。

最後に、本当にメタに残しておきたい場合は、exist ステートメントを使用してそこに移動する方がよい場合があります。

select val
from unnest(array['Known', 'Sizes', 'Go', 'Here']::text[]) as val
where exists (
    select 1
    from meta as m1
    join meta as m2 on m2."productId" = m1."productId"
                   and m2."key" = 'source'
                   and m2."value" = 'ibm'
    join meta as m3 on m3."productId" = m1."productId"
                   and m3."key" = 'year'
                   and m3."value" between 1920 and 2010
    where m1."key" = 'size'
      and m1."value" = val
  );

そうすることで、高価なgroup bysortおよびunique操作を節約できます。

于 2013-05-13T09:55:14.603 に答える