在唯一约束之前清理SQL数据

sja*_*ski 4 sql postgresql performance unique duplicate-removal

我想在对两列放置唯一约束之前清理表中的一些数据.

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 ????
Run Code Online (Sandbox Code Playgroud)

输出应该是 2,6,7

我正在寻找第一个重复之后的所有行b,c

EX:

  • 第1,2行有(2,3)为b,c第1行是可以的,因为它是第一个,2不是.

  • 第4,6,7行有(4,4)为b,c第4行是可以的,因为它是第一个,6,7不是.

然后我会:

DELETE FROM test WHERE a = those IDs;
Run Code Online (Sandbox Code Playgroud)

..并添加唯一约束.

我正在考虑与自己进行测试相交,但不确定从那里开始做什么.

Erw*_*ter 5

我做了几个测试.EXISTS事实证明,该变体要快得多 - 正如我所预料的那样,与@Tometzky所发布的相反.

PostgreSQL 9.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);
Run Code Online (Sandbox Code Playgroud)

在第一轮和第二轮测试之间,我跑了:

ANALYZE test;
Run Code Online (Sandbox Code Playgroud)

当我最终应用DELETE时,删除了3368个重复项.如果你有更多或更少的重复,性能可能会有所不同.

我用了几次运行每个查询EXPLAIN ANALYZE并取得了最好的结果.一般来说,最好的几乎与第一个或最差的不同.
SELECT(没有DELETE)显示类似的结果.

1. CTE与 rank()

总运行时间:150.411 ms
总运行时间:149.853 ms - 在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;
Run Code Online (Sandbox Code Playgroud)

2. CTE与 row_number()

总运行时间:148.240 ms
总运行时间:147.711 ms - 在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;
Run Code Online (Sandbox Code Playgroud)

3. row_number()在子查询中

总运行时间:134.753 ms
总运行时间:134.298 ms - 在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;
Run Code Online (Sandbox Code Playgroud)

4. EXISTS半连接

总运行时间:143.777 ms
总运行时间:69.072 ms - 在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)
    );
Run Code Online (Sandbox Code Playgroud)

第二轮的差异来自于切换到Hash Semi Join 而不是额外的Sort + Merge Semi Join.

结果

  • EXISTS 显然可以通过up-tp-date表统计获胜.
  • row_number()子查询中的过时统计信息最快.
  • rank() 是最慢的变种.
  • CTE比子查询慢.
  • ANALYZE(更新的统计数据)有助于提高性能并可以提供很多帮 Autovacuum(默认值)应该或多或少地自动处理 - 除了临时表或在表的主要更改之后立即.在这里这里阅读更多.

用100.000行测试

我用100.000行和63045个重复项重复测试.类似的结果,除了那个EXISTS更慢,甚至之后ANALYZE.

  1. 总运行时间:1648.601 ms
  2. 总运行时间:1623.759毫秒
  3. 总运行时间:1568.893 ms
  4. 总运行时间:1692.249 ms

将统计目标提升到1000然后最高为10000(实际超出现金),另一个ANALYZE将所有查询加速约1%,但查询规划器仍然使用Sort + Merge Semi Join for EXISTS.

ALTER TABLE test ALTER COLUMN b SET STATISTICS 10000;
ALTER TABLE test ALTER COLUMN c SET STATISTICS 10000;
ANALYZE test;
Run Code Online (Sandbox Code Playgroud)

只有在我强迫规划人员避免合并加入后,规划人员再次使用Hash Semi Join一半时间:

SET enable_mergejoin = off
Run Code Online (Sandbox Code Playgroud)
  1. 总运行时间:850.615毫秒

更新

从那时起,对查询规划器进行了改进.直接使用Hash Semi加入 PostgreSQL 9.1.7进行重新测试.