从表中删除没有唯一键的重复行

Pau*_*lor 5 sql postgresql duplicates duplicate-removal

如何在Postgres 9表中删除重复行,每个字段上的行完全重复,并且没有单独的字段可以用作唯一键,所以我不能只GROUP BY使用列并使用NOT IN语句.

我正在寻找一个单独的SQL语句,而不是一个需要我创建临时表并将记录插入其中的解决方案.我知道如何做到这一点,但需要更多的工作来适应我的自动化过程.

表定义:

jthinksearch=> \d releases_labels;
Unlogged table "discogs.releases_labels"
   Column   |  Type   | Modifiers
------------+---------+-----------
 label      | text    |
 release_id | integer |
 catno      | text    |
Indexes:
    "releases_labels_catno_idx" btree (catno)
    "releases_labels_name_idx" btree (label)
Foreign-key constraints:
    "foreign_did" FOREIGN KEY (release_id) REFERENCES release(id)
Run Code Online (Sandbox Code Playgroud)

样本数据:

jthinksearch=> select * from releases_labels  where release_id=6155;
    label     | release_id |   catno
--------------+------------+------------
 Warp Records |       6155 | WAP 39 CDR
 Warp Records |       6155 | WAP 39 CDR
Run Code Online (Sandbox Code Playgroud)

Nic*_*nes 8

如果你能负担得起重写整个表,这可能是最简单的方法:

WITH Deleted AS (
  DELETE FROM discogs.releases_labels
  RETURNING *
)
INSERT INTO discogs.releases_labels
SELECT DISTINCT * FROM Deleted
Run Code Online (Sandbox Code Playgroud)

如果需要专门定位重复记录,可以使用ctid唯一标识行的内部字段:

DELETE FROM discogs.releases_labels
WHERE ctid NOT IN (
  SELECT MIN(ctid)
  FROM discogs.releases_labels
  GROUP BY label, release_id, catno
)
Run Code Online (Sandbox Code Playgroud)

要小心ctid; 它随着时间而变化.但是你可以依赖它在单个语句的范围内保持不变.


Erw*_*ter 5

单个SQL语句

这是就地删除重复项的解决方案:

DELETE FROM releases_labels r
WHERE  EXISTS (
   SELECT 1
   FROM   releases_labels r1
   WHERE  r1 = r
   AND    r1.ctid < r.ctid
   );
Run Code Online (Sandbox Code Playgroud)

由于没有唯一的键,我(ab)使用元组 IDctid来达到目的。在每组愚人中,物理上的第一行都幸存下来。

ctid是一个系统列,不属于关联行类型的一部分,因此在表达式 中引用带有表别名的整行时r1 = r,仅比较可见ctid列(不比较 the或其他列)。这就是为什么整行可以相等并且一个ctid仍然比另一个小。

由于只有很少的重复项,这也是所有解决方案中最快的。
由于存在大量重复项,其他解决方案速度更快。

那么我建议:

ALTER TABLE discogs.releases_labels ADD COLUMN releases_labels_id serial PRIMARY KEY;
Run Code Online (Sandbox Code Playgroud)

为什么它适用于 NULL 值?

这有点令人惊讶。原因在手册中的“复合类型比较”一章中进行了解释:

如果结果取决于比较两个 NULL 值或 NULL 和非 NULL,则 SQL 规范要求按行比较返回 NULL。PostgreSQL 仅在比较两个行构造函数的结果(如第 9.23.5 节)或将行构造函数与子查询的输出进行比较(如第 9.22 节)时才执行此操作。在比较两个复合类型值的其他上下文中,两个 NULL 字段值被视为相等,并且 NULL 被视为大于非 NULL。为了使复合类型具有一致的排序和索引行为,这是必要的。

大胆强调我的。

第二个表的替代方案

我删除了该部分,因为@Nick 提供的具有数据修改 CTE 的解决方案更好。