4

2つの列に一意性制約を設定する前に、テーブル内のいくつかのデータをクリーンアップしたいと思います。

CREATE TABLE test (
 a integer NOT NULL,
 b integer NOT NULL,
 c integer NOT NULL,
 CONSTRAINT a_pk PRIMARY KEY (a)
);

INSERT INTO test (a,b,c) VALUES
 (1,2,3)
,(2,2,3)
,(3,4,3)
,(4,4,4)
,(5,4,5)
,(6,4,4)
,(7,4,4);

-- SELECT a FROM test WHERE ????

出力は2,6,7

重複している最初の行以降のすべての行を探していますb,c

元:

  • 行1、2はb、cとして(2,3)を持ちます。行1は最初であるため問題ありませんが、2はそうではありません。

  • 行4、6、7には(4,4)がb、cとして含まれています。行4は最初であるため問題ありませんが、6、7はそうではありません。

その後、私は:

DELETE FROM test WHERE a = those IDs;

..そして一意の制約を追加します。

私はそれ自体とのテストで交差することを考えていましたが、そこからどこに行くべきかわかりませんでした。

4

4 に答える 4

5

私はいくつかのテストを実行しました。EXISTSバリアントはかなり高速であることがわかります-私が予想したように、@Tometzkyが投稿したものとは反対です

適切な設定でPostgreSQL9.1.2の10.000行のテストベッド:

CREATE TEMP TABLE test (
  a serial
 ,b int NOT NULL
 ,c int NOT NULL
);

INSERT INTO test (b,c)
SELECT (random()* 100)::int AS b, (random()* 100)::int AS c
FROM   generate_series(1, 10000);

ALTER TABLE test ADD CONSTRAINT a_pk PRIMARY KEY (a);

テストの第1ラウンドと第2ラウンドの間に、次のコマンドを実行しました。

ANALYZE test;

最終的にDELETEを適用すると、3368個の重複が削除されました。重複が大幅に多い場合や少ない場合は、パフォーマンスが異なる場合があります。

で各クエリを数回実行しEXPLAIN ANALYZE、最良の結果を得ました。一般的に、最良のものは最初のものまたは最悪のものとほとんど変わりません。
SELECT(なしDELETE)でも同様の結果が得られます。

1.CTEとrank()

合計実行時間:150.411ミリ秒
合計実行時間:149.853ミリ秒-ANALYZE後

WITH x AS (
    SELECT a
          ,rank() OVER (PARTITION BY b, c ORDER BY a) AS rk
    FROM   test
    )
DELETE FROM test
USING  x
WHERE  x.a = test.a
AND    rk > 1;

2.CTEとrow_number()

合計実行時間:148.240ミリ秒
合計実行時間:147.711ミリ秒-ANALYZE後

WITH x AS (
    SELECT a
          ,row_number() OVER (PARTITION BY b, c ORDER BY a) AS rn
    FROM   test
    )
DELETE FROM test
USING  x
WHERE  x.a = test.a
AND    rn > 1;

3.row_number()サブクエリで

合計実行時間:134.753ミリ秒
合計実行時間:134.298ミリ秒-ANALYZE後

DELETE FROM test
USING (
    SELECT a
          ,row_number() OVER (PARTITION BY b, c ORDER BY a) AS rn
    FROM   test
    )  x
WHERE  x.a = test.a
AND    rn > 1;

4.EXISTSセミジョイン

合計実行時間:143.777ミリ秒
合計実行時間:69.072ミリ秒-ANALYZE

DELETE FROM test t
WHERE EXISTS (
    SELECT 1
    FROM   test t1
    WHERE  t1.a < t.a
    AND   (t1.b, t1.c) = (t.b, t.c)
    );

2回目の実行での違いは、追加のソート+マージセミジョインではなく、ハッシュセミジョインに切り替えることです。

結果

  • EXISTS明らかにup-tp-dateテーブル統計で勝ちます。
  • サブクエリに古い統計row_number()があると、最速になります。
  • rank()最も遅いバリアントです。
  • CTEはサブクエリよりも低速です。
  • ANALYZE(更新された統計)はパフォーマンスを助け、大いに役立ちます。Autovacuum(デフォルト)は、一時的なテーブルまたはテーブルへの大きな変更の直後を除いて、多かれ少なかれこれを自動的に処理する必要があります。詳細はこちらまたはこちらをご覧ください。

100.000行でテストする

100.000行と63045の複製でテストを繰り返しました。EXISTS後でも遅いことを除いて、同様の結果ANALYZE

  1. 総実行時間:1648.601ミリ秒
  2. 総実行時間:1623.759ミリ秒
  3. 総実行時間:1568.893ミリ秒
  4. 総実行時間:1692.249ミリ秒

統計ターゲットを1000に上げてから最大10000(実際のライブではやり過ぎ)に上げると、別ANALYZEのクエリはすべてのクエリを最大1%高速化しましたが、クエリプランナーは引き続きSort + Merge SemiJoinforを使用しましEXISTSた。

ALTER TABLE test ALTER COLUMN b SET STATISTICS 10000;
ALTER TABLE test ALTER COLUMN c SET STATISTICS 10000;
ANALYZE test;

マージ結合を回避するようにプランナーに強制した後でのみ、プランナーはハッシュセミ結合を使用して再び半分の時間を費やしました。

SET enable_mergejoin = off
  1. 総実行時間:850.615ミリ秒

アップデート

それ以来、クエリプランナーが改善されていますPostgreSQL9.1.7を使用した再テストでHashSemiJoinに直接移動しました。

于 2012-05-31T22:47:29.303 に答える
3

ウィンドウ関数の使用は、この答えよりもはるかに高速であるはずです:

select a
from (
  select a, rank() over (partition by b, c order by a) as rank
  from test ) as _
where rank>1;
于 2012-05-31T16:56:40.277 に答える
2
select o.a from test o
where exists ( select 'x' 
                 from test i
                where i.c = o.c
                  and i.b = o.b
                  and i.a < o.a
            );

同僚に感謝します!

于 2012-05-31T15:42:26.590 に答える
0

私はこれを試してみます:

delete from
  test
where
  a not in (
    select   min(a)
    from     test
    group by b,c)

私のマシンでは20〜60ミリ秒の間、その価値があり、分析しても計画には影響しません。

 Delete on test  (cost=237.50..412.50 rows=5000 width=6)
   ->  Seq Scan on test  (cost=237.50..412.50 rows=5000 width=6)
         Filter: (NOT (hashed SubPlan 1))
         SubPlan 1
           ->  HashAggregate  (cost=225.00..235.00 rows=1000 width=12)
                 ->  Seq Scan on test  (cost=0.00..150.00 rows=10000 width=12)
于 2013-03-13T18:51:03.310 に答える