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)
如果你能负担得起重写整个表,这可能是最简单的方法:
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
; 它随着时间而变化.但是你可以依赖它在单个语句的范围内保持不变.
这是就地删除重复项的解决方案:
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,则 SQL 规范要求按行比较返回 NULL。PostgreSQL 仅在比较两个行构造函数的结果(如第 9.23.5 节)或将行构造函数与子查询的输出进行比较(如第 9.22 节)时才执行此操作。在比较两个复合类型值的其他上下文中,两个 NULL 字段值被视为相等,并且 NULL 被视为大于非 NULL。为了使复合类型具有一致的排序和索引行为,这是必要的。
大胆强调我的。
我删除了该部分,因为@Nick 提供的具有数据修改 CTE 的解决方案更好。