15

次のように、基本的に を含む 2 つのSELECTクエリで構成される DB ビューがありUNION ALLます。

CREATE VIEW v AS
SELECT time, etc. FROM t1 // #1...
UNION ALL
SELECT time, etc. FROM t2 // #2...

問題は、フォームを選択することです

SELECT ... FROM v WHERE time >= ... AND time < ...

その上で実行するのは非常に遅いです。

SELECT #1 と #2 はどちらもかなり高速で、適切にインデックスが付けられているなどです。ビュー v1 と v2 を次のように作成すると、次のようになります。

CREATE VIEW v1 AS
SELECT time, etc. FROM t1 // #1...

CREATE VIEW v2 AS
SELECT time, etc. FROM t2 // #2...

そして、上記と同じ WHERE 条件を使用した同じ SELECT は、個別に問題なく動作します。

どこに問題があり、それを解決する方法についてのアイデアはありますか?

(ちなみに、これは最近の Postgres バージョンの 1 つです。)

編集:匿名化されたクエリプランの追加(素晴らしいツールへのリンクについては@filipremに感謝します):

v1:

Aggregate  (cost=9825.510..9825.520 rows=1 width=53) (actual time=59.995..59.995 rows=1 loops=1)
  ->  Index Scan using delta on echo alpha  (cost=0.000..9815.880 rows=3850 width=53) (actual time=0.039..53.418 rows=33122 loops=1)
          Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey))
          Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf)))

v2:

Aggregate  (cost=15.470..15.480 rows=1 width=33) (actual time=0.231..0.231 rows=1 loops=1)
  ->  Index Scan using yankee on six charlie  (cost=0.000..15.220 rows=99 width=33) (actual time=0.035..0.186 rows=140 loops=1)
          Index Cond: (("juliet" >= 'seven'::uniform bravo oscar whiskey) AND ("juliet" <= 'november'::uniform bravo oscar whiskey))
          Filter: (NOT victor)

v:

Aggregate  (cost=47181.850..47181.860 rows=1 width=0) (actual time=37317.291..37317.291 rows=1 loops=1)
  ->  Append  (cost=42.170..47132.480 rows=3949 width=97) (actual time=1.277..37304.453 rows=33262 loops=1)
        ->  Nested Loop Left Join  (cost=42.170..47052.250 rows=3850 width=99) (actual time=1.275..37288.465 rows=33122 loops=1)
              ->  Hash Left Join  (cost=42.170..9910.990 rows=3850 width=115) (actual time=1.123..117.797 rows=33122 loops=1)
                      Hash Cond: ((alpha_seven.two)::golf = (quebec_three.two)::golf)
                    ->  Index Scan using delta on echo alpha_seven  (cost=0.000..9815.880 rows=3850 width=132) (actual time=0.038..77.866 rows=33122 loops=1)
                            Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two))
                            Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf)))
                    ->  Hash  (cost=30.410..30.410 rows=941 width=49) (actual time=1.068..1.068 rows=941 loops=1)
                            Buckets: 1024  Batches: 1  Memory Usage: 75kB
                          ->  Seq Scan on alpha_india quebec_three  (cost=0.000..30.410 rows=941 width=49) (actual time=0.010..0.486 rows=941 loops=1)
              ->  Index Scan using mike on hotel quebec_sierra  (cost=0.000..9.630 rows=1 width=24) (actual time=1.112..1.119 rows=1 loops=33122)
                      Index Cond: ((alpha_seven.zulu)::golf = (quebec_sierra.zulu)::golf)
        ->  Subquery Scan on "*SELECT* 2"  (cost=34.080..41.730 rows=99 width=38) (actual time=1.081..1.951 rows=140 loops=1)
              ->  Merge Right Join  (cost=34.080..40.740 rows=99 width=38) (actual time=1.080..1.872 rows=140 loops=1)
                      Merge Cond: ((quebec_three.two)::golf = (charlie.two)::golf)
                    ->  Index Scan using whiskey_golf on alpha_india quebec_three  (cost=0.000..174.220 rows=941 width=49) (actual time=0.017..0.122 rows=105 loops=1)
                    ->  Sort  (cost=18.500..18.750 rows=99 width=55) (actual time=0.915..0.952 rows=140 loops=1)
                            Sort Key: charlie.two
                            Sort Method:  quicksort  Memory: 44kB
                          ->  Index Scan using yankee on six charlie  (cost=0.000..15.220 rows=99 width=55) (actual time=0.022..0.175 rows=140 loops=1)
                                  Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two))
                                  Filter: (NOT victor)

julietですtime

4

8 に答える 8

10

これはパイロットエラーのケースのようです。"v" クエリ プランは、少なくとも 5 つの異なるテーブルから選択します。

さて、あなたは正しいデータベースに接続していますか? ファンキーな search_path 設定がいくつかあるのではないでしょうか? t1 と t2 は実際にはビューである可能性があります (別のスキーマにある可能性があります)。たぶん、あなたはどういうわけか間違ったビューから選択していますか?

明確化後に編集:

「結合の削除」と呼ばれる非常に新しい機能を使用しています: http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.0#Join_Removal

http://rhaas.blogspot.com/2010/06/why-join-removal-is-cool.html

ユニオンオールが関係している場合、この機能は機能しないようです。おそらく、必要な 2 つのテーブルのみを使用してビューを書き直す必要があります。

別の編集:集計を使用しているように見えます (「select count(*) from v」と「select * from v」など)。これは、結合の削除に直面して大きく異なる計画を取得する可能性があります。実際のクエリ、ビューとテーブルの定義、および使用された計画を投稿しない限り、私たちはそれほど遠くには行かないと思います...

于 2012-01-30T18:02:40.077 に答える
6

あなたのクエリは次のように実行されていると思います:

(
   ( SELECT time, etc. FROM t1 // #1... )
   UNION ALL
   ( SELECT time, etc. FROM t2 // #2... )
)
WHERE time >= ... AND time < ...

オプティマイザが最適化に苦労しているもの。つまり、句UNION ALLを適用する前に最初の処理を行っていますが、 . のに句WHEREを適用したいと考えています。WHEREUNION ALL

WHERE句を に入れられませんでしたCREATE VIEWか?

CREATE VIEW v AS
( SELECT time, etc. FROM t1  WHERE time >= ... AND time < ... )
UNION ALL
( SELECT time, etc. FROM t2  WHERE time >= ... AND time < ... )

または、ビューにWHERE句を含めることができない場合は、おそらく 2 つのビューを維持し、必要なときに句をUNION ALL使用して実行できます。WHERE

CREATE VIEW v1 AS
SELECT time, etc. FROM t1 // #1...

CREATE VIEW v2 AS
SELECT time, etc. FROM t2 // #2...

( SELECT * FROM v1 WHERE time >= ... AND time < ... )
UNION ALL
( SELECT * FROM v2 WHERE time >= ... AND time < ... )
于 2012-02-03T20:06:24.100 に答える
2

私は Postgres を知りませんが、いくつかの RMDB は、インデックスの場合、BETWEEN よりも悪い比較演算子を処理します。BETWEEN を使って試してみます。

SELECT ... FROM v WHERE time BETWEEN ... AND ...
于 2012-02-05T07:55:19.790 に答える
1

ビューを作成する代わりに、各呼び出しで新しい SQL を動的に発行し、ユニオン クエリの各 SELECT に where 句を統合する可能性があります。

SELECT time, etc. FROM t1
    WHERE time >= ... AND time < ...
UNION ALL
SELECT time, etc. FROM t2
    WHERE time >= ... AND time < ...

編集:

パラメータ化された関数を使用できますか?

CREATE OR REPLACE FUNCTION CallMyView(t1 date, t2 date)
RETURNS TABLE(d date, etc.)
AS $$
    BEGIN
        RETURN QUERY
            SELECT time, etc. FROM t1
                WHERE time >= t1 AND time < t2
            UNION ALL
            SELECT time, etc. FROM t2
                WHERE time >= t1 AND time < t2;
    END;
$$ LANGUAGE plpgsql;

電話

SELECT * FROM CallMyView(..., ...);
于 2012-01-30T18:03:29.973 に答える
1

2 つのテーブルを結合します。元のテーブルを示す列を追加します。必要に応じて、元のテーブル名を、関連する部分だけを選択するビューに置き換えます。問題が解決しました!

スーパークラス/サブクラスの db 設計パターンを調べると役立つ場合があります。

于 2012-02-06T02:01:39.890 に答える
0

Encountered same scenario on 11g:

Scenario 1:

CREATE VIEW v AS
  SELECT time, etc. FROM t1 // #1...

The following query runs fast, plan looks okay:

SELECT ... FROM v WHERE time >= ... AND time < ...

Scenario 2:

CREATE VIEW v AS
  SELECT time, etc. FROM t2 // #2...

The following query runs fast, plan looks okay:

SELECT ... FROM v WHERE time >= ... AND time < ...

Scenario 3, with UNION ALL:

CREATE VIEW v AS
  SELECT time, etc. FROM t1 // #1...
  UNION ALL
  SELECT time, etc. FROM t2 // #2...

The following runs slow. Plan breaks apart t1 and t2 (which were also views) and assembles them as a big series of unions. The time filters are being applied properly on the individual components, but it is still very slow:

SELECT ... FROM v WHERE time >= ... AND time < ...

I would have been happy to just get a time in the ballpark of t1 plus t2, but it was more than double. Adding the parallel hint did the trick for me in this case. It re-arranged everything into a better plan:

SELECT /*+ parallel */ ... FROM v WHERE time >= ... AND time < ...
于 2015-05-06T20:12:39.070 に答える
-3

コメントとして投稿するポイントがあまりないと思うので、回答として投稿します

PostgreSQL が舞台裏でどのように機能しているかはわかりません。もしそれが Oracle だったら手がかりが得られると思います。そこで、Oracle がどのように機能するかをここに示します。

UNION ALLビューは遅くなります。これは、舞台裏でSELECT #1#2の両方のレコードが最初に一時テーブルに結合され、その場で作成され、次にSELECT ... FROM v WHERE time >= . .. AND time < ...は、この一時テーブルで実行されます。#1#2の両方がインデックス化されているため、期待どおりに個別に高速に動作していますが、この一時テーブルは (もちろん) インデックス化されておらず、最終的なレコードはこの一時テーブルから選択されているため、応答が遅くなります。

今、少なくとも、それをより速く + ビュー + 非実体化する方法がわかりません

SELECT #1#2および UNION を明示的に実行する以外の 1 つの方法は、アプリケーション プログラミング言語でストアド プロシージャまたは関数を使用することです (該当する場合)。各インデックス付きテーブルを個別に呼び出してから結果を結合します。これはSELECT ... FROM v WHERE time >= ... AND time < ... :(ほど単純ではありません

于 2012-01-30T17:52:22.147 に答える