gjr*_*ber 93 sql postgresql duplicate-removal unique-constraint sql-delete
我必须为现有表添加唯一约束.这很好,除了表已经有数百万行,并且许多行违反了我需要添加的唯一约束.
删除有问题的行的最快方法是什么?我有一个SQL语句,它找到重复项并删除它们,但它需要永远运行.有没有其他方法可以解决这个问题?也许备份表,然后在添加约束后恢复?
小智 173
其中一些方法看起来有点复杂,我通常这样做:
给定表table
,想要在(field1,field2)上保持唯一,保持行与max field3:
DELETE FROM table USING table alias
WHERE table.field1 = alias.field1 AND table.field2 = alias.field2 AND
table.max_field < alias.max_field
Run Code Online (Sandbox Code Playgroud)
例如,我有一个表,user_accounts
我想在电子邮件中添加一个唯一约束,但我有一些重复.还要说我想保留最近创建的一个(重复项中的最大ID).
DELETE FROM user_accounts USING user_accounts ua2
WHERE user_accounts.email = ua2.email AND user_account.id < ua2.id;
Run Code Online (Sandbox Code Playgroud)
USING
不是标准的SQL,它是PostgreSQL扩展(但是非常有用),但原始问题特别提到了PostgreSQL.jus*_*ody 101
例如,您可以:
CREATE TABLE tmp ...
INSERT INTO tmp SELECT DISTINCT * FROM t;
DROP TABLE t;
ALTER TABLE tmp RENAME TO t;
Run Code Online (Sandbox Code Playgroud)
Erw*_*ter 25
您可以在截断后将唯一行重新插入到同一个表中,而不是创建新表.在一次交易中完成所有工作.(可选)您可以使用自动删除事务结束时的临时表ON COMMIT DROP
.见下文.
这种方法仅在需要从表中删除大量行时才有用.只需几个重复,使用普通DELETE
.
你提到了数百万行.要使操作快速,您需要为会话分配足够的临时缓冲区.必须在当前会话中使用任何临时缓冲区之前调整设置.找出你桌子的大小:
SELECT pg_size_pretty(pg_relation_size('tbl'));
Run Code Online (Sandbox Code Playgroud)
相应地设定temp_buffers
.慷慨解囊,因为内存中的表示需要更多的RAM.
SET temp_buffers = 200MB; -- example value
BEGIN;
-- CREATE TEMPORARY TABLE t_tmp ON COMMIT DROP AS -- drop temp table at commit
CREATE TEMPORARY TABLE t_tmp AS -- retain temp table after commit
SELECT DISTINCT * FROM tbl; -- DISTINCT folds duplicates
TRUNCATE tbl;
INSERT INTO tbl
SELECT * FROM t_tmp;
-- ORDER BY id; -- optionally "cluster" data while being at it.
COMMIT;
Run Code Online (Sandbox Code Playgroud)
如果存在依赖对象,则此方法优于创建新表.引用该表的视图,索引,外键或其他对象.TRUNCATE
让你用干净的石板开始呢(在后台新的文件),并且是多快于DELETE FROM tbl
大表(DELETE
实际上可以用小桌子更快).
对于大表,删除索引和外键,重新填充表并重新创建这些对象通常会更快.至于fk约束,你必须确定新数据当然是有效的,否则你会在尝试创建fk时遇到异常.
请注意,TRUNCATE
需要更积极的锁定DELETE
.对于具有大量并发负载的表,这可能是一个问题.
如果TRUNCATE
不是一个选项,或者通常用于中小型表,那么有一种类似的技术可以使用数据修改CTE(Postgres 9.1 +):
WITH del AS (DELETE FROM tbl RETURNING *)
INSERT INTO tbl
SELECT DISTINCT * FROM del;
-- ORDER BY id; -- optionally "cluster" data while being at it.
Run Code Online (Sandbox Code Playgroud)
大桌子较慢,因为TRUNCATE
那里更快.但对于小型表来说可能更快(也更简单!).
如果你根本没有依赖对象,你可以创建一个新表并删除旧表,但是你很难通过这种通用方法获得任何东西.
对于不适合可用RAM的非常大的表,创建新表将会快得多.你必须权衡这与可能的依赖对象的麻烦/开销.
Jan*_*rek 20
您可以使用oid或ctid,它通常是表中的"不可见"列:
DELETE FROM table
WHERE ctid NOT IN
(SELECT MAX(s.ctid)
FROM table s
GROUP BY s.column_has_be_distinct);
Run Code Online (Sandbox Code Playgroud)
she*_*kwi 19
PostgreSQL窗口函数对于这个问题很方便.
DELETE FROM tablename
WHERE id IN (SELECT id
FROM (SELECT id,
row_number() over (partition BY column1, column2, column3 ORDER BY id) AS rnum
FROM tablename) t
WHERE t.rnum > 1);
Run Code Online (Sandbox Code Playgroud)
请参阅删除重复项.
删除重复项的通用查询:
DELETE FROM table_name
WHERE ctid NOT IN (
SELECT max(ctid) FROM table_name
GROUP BY column1, [column 2, ...]
);
Run Code Online (Sandbox Code Playgroud)
该列ctid
是可用于每个表的特殊列,但除非特别提及,否则不可见.该ctid
列的值被认为是表中的每一行都是唯一的.
create table test ( a text, b text );
Run Code Online (Sandbox Code Playgroud)
insert into test values ( 'x', 'y');
insert into test values ( 'x', 'x');
insert into test values ( 'y', 'y' );
insert into test values ( 'y', 'x' );
Run Code Online (Sandbox Code Playgroud)
insert into test values ( 'x', 'y');
insert into test values ( 'x', 'x');
insert into test values ( 'y', 'y' );
insert into test values ( 'y', 'x' );
Run Code Online (Sandbox Code Playgroud)
insert into test values ( 'x', 'y');
select oid, a, b from test;
Run Code Online (Sandbox Code Playgroud)
select o.oid, o.a, o.b from test o
where exists ( select 'x'
from test i
where i.a = o.a
and i.b = o.b
and i.oid < o.oid
);
Run Code Online (Sandbox Code Playgroud)
注意:PostgreSQL不支持from
删除子句中提到的表上的别名.
delete from test
where exists ( select 'x'
from test i
where i.a = test.a
and i.b = test.b
and i.oid < test.oid
);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
88668 次 |
最近记录: |