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

时间:2012-05-31 15:34:44

标签: sql performance postgresql 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 ????

输出应为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;

..并添加唯一约束。

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

4 个答案:

答案 0 :(得分:5)

我跑了几个测试。事实证明EXISTS变体要快得多 - 正如我预期的那样,与what @Tometzky posted相反。

在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);

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

ANALYZE test;

当我最终应用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;

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;

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;

4。 EXISTS半连接

总运行时间:143.777毫秒 总运行时间: 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)
    );

第二轮的差异来自于切换到 Hash Semi Join 而不是额外的排序+合并半连接

结果

  • EXISTS显然赢得了up-tp-date表统计信息。
  • 子查询中过时的统计信息row_number()最快。
  • rank()是最慢的变体。
  • CTE比子查询慢。
  • ANALYZE(更新的统计信息)有助于提高性能,可以帮助很多。 Autovacuum(默认值)应该或多或少地自动处理 - 除了临时表或者在对表进行重大更改后立即执行此操作。阅读更多herehere

使用100.000行测试

我用100.000行和63045个重复项重复测试。类似的结果,除了EXISTS更慢,即使在ANALYZE之后。

  1. 总运行时间:1648.601 ms
  2. 总运行时间:1623.759 ms
  3. 总运行时间:1568.893 ms
  4. 总运行时间:1692.249 ms
  5. 将统计信息目标提升至1000,然后提高到最大值10000(实际工作量过大),另一个ANALYZE将所有查询加速约1%,但查询规划器仍然使用 Sort + EXISTS合并半连接

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

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

    SET enable_mergejoin = off
    
    1. 总运行时间:850.615毫秒
    2. 更新

      从那时起,对查询计划程序进行了改进。使用PostgreSQL 9.1.7重新测试时直接 Hash Semi Join

答案 1 :(得分:3)

使用window functions应该比this answer快得多:

select a
from (
  select a, rank() over (partition by b, c order by a) as rank
  from test ) as _
where rank>1;

答案 2 :(得分: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
            );

感谢同事!

答案 3 :(得分: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)