web-dev-qa-db-ja.com

PostgreSQL 9.1-VIEWSでのクエリに時間がかかる

PostgreSQL 9.1を使用すると、PostgreSQLのVIEWでクエリを実行中に問題が発生します。状況は次のとおりです。

ビュー「Swiss_client_wise_minutes_and_profit」を構築したパーティションテーブル「buz_scdr」があります。このVIEWの目的は、「buz_scdr」テーブルを含むさまざまなテーブルのデータを結合して、効率的なクエリを作成することです。この戦略は、テーブル "buz_scdr"が巨大になるまで正常に機能していました(すべてのパーティションのすべてのレコードが巨大になりました。テーブルは日付に基づいてパーティション化されます)。

このVIEWで実行されているクエリは、非常に長い時間(約5〜10分)かかりました。このクエリの実行に時間がかかる理由を理解するために、EXPLAINコマンドを使用してその実行プランを表示しました。使用したクエリは次のとおりです。

EXPLAIN SELECT * from  "Swiss_client_wise_minutes_and_profit" where start_time = '2012-7-22 08:00';

結果は explain.depesz.comで または次のようになります。

   Subquery Scan on "Swiss_client_wise_minutes_and_profit"  (cost=2127919.71..94874537.55 rows=40474 width=677)
   Filter: ("Swiss_client_wise_minutes_and_profit".start_time = '2012-07-22 08:00:00+00'::timestamp with time zone)
   ->  WindowAgg  (cost=2127919.71..94773352.06 rows=8094839 width=148)
         ->  Sort  (cost=2127919.71..2148156.81 rows=8094839 width=148)
               Sort Key: cc.name, rdga.group_id
               ->  Hash Left Join  (cost=1661.50..604234.77 rows=8094839 width=148)
                     Hash Cond: (((cc.company_id)::text = (rdga.company_id)::text) AND ((cs.c_prefix_id)::text = (rdga.dest_id)::text))
                     ->  Hash Left Join  (cost=7.88..460615.39 rows=8094839 width=123)
                           Hash Cond: ((cs.client_name_id)::text = (cc."Alias_name")::text)
                           ->  Append  (cost=0.00..349303.48 rows=8094839 width=111)
                                 ->  Seq Scan on "Swiss_buz_scdr" cs  (cost=0.00..1.06 rows=1 width=610)
                                       Filter: ((customer_name)::text = 'SSP Root'::text)
                                 ->  Seq Scan on scdr_buz__2012_07_11 cs  (cost=0.00..349302.41 rows=8094838 width=111)
                                       Filter: ((customer_name)::text = 'SSP Root'::text)
                           ->  Hash  (cost=5.17..5.17 rows=217 width=24)
                                 ->  Seq Scan on "Corporate_companyalias" cc  (cost=0.00..5.17 rows=217 width=24)
                     ->  Hash  (cost=1334.42..1334.42 rows=21280 width=50)
                           ->  Hash Join  (cost=169.56..1334.42 rows=21280 width=50)
                                 Hash Cond: ((rdga.company_id)::text = (c.name)::text)
                                 ->  Hash Join  (cost=162.68..1034.93 rows=21280 width=50)
                                       Hash Cond: ((rdga.group_id)::text = (rdg.name)::text)
                                       ->  Seq Scan on "RateManagement_destgroupassign" rdga  (cost=0.00..497.35 rows=25935 width=40)
                                       ->  Hash  (cost=123.64..123.64 rows=3123 width=32)
                                             ->  Hash Join  (cost=13.08..123.64 rows=3123 width=32)
                                                   Hash Cond: (rdg.country_id = cc.id)
                                                   ->  Seq Scan on "RateManagement_destinationgroup" rdg  (cost=0.00..65.06 rows=3806 width=26)
                                                   ->  Hash  (cost=7.48..7.48 rows=448 width=14)
                                                         ->  Seq Scan on "Corporate_country" cc  (cost=0.00..7.48 rows=448 width=14)
                                 ->  Hash  (cost=4.17..4.17 rows=217 width=16)
                                       ->  Seq Scan on "Corporate_company" c  (cost=0.00..4.17 rows=217 width=16)
         SubPlan 1
           ->  Seq Scan on "Corporate_companyalias" cc  (cost=0.00..5.71 rows=1 width=12)
                 Filter: (("Alias_name")::text = (cs.client_name_id)::text)
         SubPlan 2
           ->  Seq Scan on "Corporate_companyalias" cc  (cost=0.00..5.71 rows=1 width=12)
                 Filter: (("Alias_name")::text = (cs.vendor_name_id)::text)
(36 rows)

上記のEXPLAINコマンドの結果は、クエリが、合計8094838レコードを含む "buz_scdr"テーブル(上記で強調表示)を順次スキャンしていることを示しています。 VIEWのクエリは、「buz_scdr」のパーティション制約(日付)に従っていないため、テーブル全体がスキャンされます。

実験の目的で、日付と時刻とともにWHEREステートメントを使用して "buz_scdr" TABLEでクエリを直接実行しましたが、パーティションの制約を正しく尊重し、テーブル全体をスキャンしませんでした。これは、パーティション分割されたテーブルで直接実行されたクエリは期待どおりに機能しているが、その上に構築されたVIEWに問題があることを示しています。

これはPostgreSQLビューのグローバルな問題ですか、それとも何かを見逃しましたか?

編集:以下は、VIEW "Swiss_client_wise_minutes_and_profit"のDDLです。

CREATE VIEW "Swiss_client_wise_minutes_and_profit" 
    AS SELECT ROW_NUMBER() OVER (ORDER BY rp.country, rp.destination) 
    As id, (SELECT company_id FROM  "Corporate_companyalias" 
    AS cc WHERE cc."Alias_name" = client_name_id) 
    AS client_name, (SELECT company_id FROM  "Corporate_companyalias" 
    AS cc WHERE cc."Alias_name" = vendor_name_id) AS vendor_name, cs.c_prefix_id 
    AS c_prefix, cs.v_prefix_id AS v_prefix, rp.country, rp.destination, cs.c_total_calls, cs.v_total_calls, cs.successful_calls, cs.billed_duration, cs.v_billed_amount AS cost, cs.c_billed_amount 
    AS revenue, cs.c_pdd AS pdd, cs.profit, cs.start_time, cs.end_time, cs.switch_name FROM "Swiss_buz_scdr" 
    AS cs LEFT JOIN "Corporate_companyalias" AS cc ON cs.client_name_id = cc."Alias_name" LEFT JOIN "RateManagement_prefix_and_client_wise_destinationgroup" 
    AS rp ON rp.client_name = cc.company_id AND rp.prefix = cs.c_prefix_id WHERE cs.customer_name = 'SSP Root';

編集2:以下は、「EXPLAIN ANALYZE」コマンドの出力のリンクです。

PastebinのEXPLAIN ANALYZE出力

3

クエリを適切にフォーマットするすると、何が起こっているかを確認するのに役立ちます。私はあなたのクエリを調査し、不審なSQLを見つけました:


CREATE VIEW "Swiss_client_wise_minutes_and_profit" AS
SELECT ROW_NUMBER() OVER (ORDER BY rp.country, rp.destination) AS id
     , (SELECT company_id
        FROM  "Corporate_companyalias" AS cc
        WHERE  cc."Alias_name" = client_name_id) AS client_name
     , (SELECT company_id
        FROM  "Corporate_companyalias" AS cc
        WHERE  cc."Alias_name" = vendor_name_id) AS vendor_name
     , cs.c_prefix_id AS c_prefix
     , cs.v_prefix_id AS v_prefix
     , rp.country
     , rp.destination
     , cs.c_total_calls
     , cs.v_total_calls
     , cs.successful_calls
     , cs.billed_duration
     , cs.v_billed_amount AS cost
     , cs.c_billed_amount AS revenue
     , cs.c_pdd AS pdd
     , cs.profit
     , cs.start_time
     , cs.end_time
     , cs.switch_name
FROM   "Swiss_buz_scdr" AS cs
LEFT   JOIN "Corporate_companyalias" AS cc ON cs.client_name_id = cc."Alias_name"
LEFT   JOIN "RateManagement_prefix_and_client_wise_destinationgroup" AS rp
         ON rp.client_name = cc.company_id AND rp.prefix = cs.c_prefix_id
WHERE  cs.customer_name = 'SSP Root';
  • 外側と内側のccで同じテーブルエイリアスSELECTを使用しないでください。それは違法ではありませんが、あなたを混乱させるのに役立ちます。

  • client_name_idvendor_name_idがどこにバインドされるか、外部クエリへの参照のテーブル修飾がないとわかりません。テーブルの定義を知る必要がありますが、結果はCROSS JOINsになると思います。これは、おそらく意図したものではなく、問題の根本原因でもありません。

correlated subqueriesは単純な式として書き直すことができると思います。多分それは別のJOINを必要とします。ここに私の...

あなたが実際に何を求めているかを学んだ。

CREATE VIEW "Swiss_client_wise_minutes_and_profit" AS
SELECT ROW_NUMBER() OVER (ORDER BY r.country, r.destination) AS id
     , c.company_id AS client_name
     , v.company_id AS vendor_name
     , s.c_prefix_id AS c_prefix
     , s.v_prefix_id AS v_prefix
     , r.country
     , r.destination
     , s.c_total_calls
     , s.v_total_calls
     , s.successful_calls
     , s.billed_duration
     , s.v_billed_amount AS cost
     , s.c_billed_amount AS revenue
     , s.c_pdd AS pdd
     , s.profit
     , s.start_time
     , s.end_time
     , s.switch_name
FROM   "Swiss_buz_scdr" s
LEFT   JOIN "Corporate_companyalias" c ON c."Alias_name" = s.client_name_id
LEFT   JOIN "RateManagement_prefix_and_client_wise_destinationgroup" r
         ON r.client_name = c.company_id AND r.prefix = s.c_prefix_id
LEFT   JOIN "Corporate_companyalias" v ON v."Alias_name" = s.vendor_name_id
WHERE  s.customer_name = 'SSP Root';

余談:"RateManagement_prefix_and_client_wise_destinationgroup"より短い名前を目指します。できれば、二重引用符を必要としない合法的な小文字の名前。

5