【问题标题】:Most efficient way to remove duplicates - Postgres删除重复项的最有效方法 - Postgres
【发布时间】:2018-12-11 19:54:28
【问题描述】:

我总是用这种查询删除重复项:

delete from test a
using test b 
where a.ctid < b.ctid
and a.col1=b.col1
and a.col2=b.col2
and a.col3=b.col3

另外,我看到有人使用了这个查询:

DELETE FROM test WHERE test.ctid NOT IN 
(SELECT ctid FROM (
    SELECT DISTINCT ON (col1, col2) *
  FROM test));

甚至这个(重复,直到你用完重复):

delete from test ju where ju.ctid in 
(select ctid from (
select  distinct on (col1, col2) * from test ou
where (select count(*) from test inr
where inr.col1= ou.col1 and inr.col2=ou.col2) > 1

现在我遇到了一个包含 500 万行的表,其中的列中的索引将在 where 子句中匹配。现在我想知道:

在所有那些显然做同样事情的方法中,哪种方法最有效,为什么? 我只运行第二个,删除重复项需要 45 多分钟。我只是好奇哪个是最有效的,以防我必须从另一个大表中删除重复项。是否有主键并不重要,您始终可以创建它。

【问题讨论】:

  • ctid 值的比较很慢,你有另一种唯一标识行的方法吗?也许是serial(或identity)专栏?
  • 我总是可以创建一个序列号,这没问题。我刚刚在运行 45 分钟后杀死了第二个示例,而第一个示例仅在 50 秒后完成。我很确定他们都删除了重复项,只留下一行。既然两者都做同样的事情,为什么会出现如此巨大的时差?

标签: postgresql duplicates


【解决方案1】:

demo:db<>fiddle

使用row_number()window function可以轻松实现查找重复:

SELECT ctid 
FROM(
    SELECT 
        *, 
        ctid,
        row_number() OVER (PARTITION BY col1, col2, col3 ORDER BY ctid) 
    FROM test
)s
WHERE row_number >= 2

此命令对绑定的行进行分组并添加行计数器。所以row_number &gt; 1 的每一行都是重复的,可以删除:

DELETE 
FROM test
WHERE ctid IN 
(
    SELECT ctid 
    FROM(
        SELECT 
            *, 
            ctid,
            row_number() OVER (PARTITION BY col1, col2, col3 ORDER BY ctid) 
        FROM test
    )s
    WHERE row_number >= 2
)

我不知道这个解决方案是否比您的尝试更快,但您可以尝试一下。

此外 - 正如 @a_horse_with_no_name 已经说过的 - 我建议使用自己的标识符而不是 ctid 来解决性能问题。


编辑:

对于我的测试数据,您的第一个版本似乎比我的解决方案快一点。您的第二个版本似乎较慢,您的第三个版本对我不起作用(修复编译错误后它没有显示结果)。

demo:db<>fiddle

【讨论】:

  • 我已经测试了你的版本,它比我尝试的第一个版本更好,因为出于某种原因,我的版本没有删除一些重复项。另外,非常感谢,我不知道分区。我不太明白您的查询如何不删除所有重复的行,但它为每组重复项留下一行,但它可以工作。
  • 请看看我的小提琴。第二部分显示了 row_number 的作用。它为每个重复的行添加一个从 1 到 3 的计数器。因此,row_number = 1 的第一行可以看作是“原始”行,后面的都是重复的。下一步(第 3 部分)是过滤所有 row_number >= 2(或 NOT 1)的行。所以第一行没有被选中,而是所有其他的。这些选定的可以删除。 row_number = 1 行保留。
  • 感谢您的解释。我在我的桌子上测试了它,它可以工作。
  • 太棒了!但是“WHERE ctid IN”对于我的 70kk 表来说太慢了,使用 PRIMARY KEY 比 ctid 快得多。
猜你喜欢
  • 1970-01-01
  • 2018-04-02
  • 2015-08-02
  • 2017-10-18
  • 2016-09-06
  • 1970-01-01
  • 2020-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多