我的表中有重复的行,我想以最有效的方式删除重复项,因为表很大。经过一番研究,我得出了这个问题:
WITH TempEmp AS
(
SELECT name, ROW_NUMBER() OVER(PARTITION by name, address, zipcode ORDER BY name) AS duplicateRecCount
FROM mytable
)
-- Now Delete Duplicate Records
DELETE FROM TempEmp
WHERE duplicateRecCount > 1;
但它只适用于SQL,而不适用于Netezza。它似乎不喜欢DELETE
子句之后的WITH
?
答案 0 :(得分:52)
如果您没有其他唯一标识符,则可以使用ctid
:
delete from mytable
where exists (select 1
from mytable t2
where t2.name = mytable.name and
t2.address = mytable.address and
t2.zip = mytable.zip and
t2.ctid > mytable.ctid
);
在每个表中都有一个唯一的自动递增ID是个好主意。像这样做delete
是一个重要原因。
答案 1 :(得分:28)
我喜欢@ erwin-brandstetter的解决方案,但希望使用USING
关键字显示解决方案:
DELETE FROM table_with_dups T1
USING table_with_dups T2
WHERE T1.ctid < T2.ctid -- delete the "older" ones
AND T1.name = T2.name -- list columns that define duplicates
AND T1.address = T2.address
AND T1.zipcode = T2.zipcode;
如果您想在删除记录之前查看记录,只需将DELETE
替换为SELECT *
,将USING
替换为逗号,
,即
SELECT * FROM table_with_dups T1
, table_with_dups T2
WHERE T1.ctid < T2.ctid -- select the "older" ones
AND T1.name = T2.name -- list columns that define duplicates
AND T1.address = T2.address
AND T1.zipcode = T2.zipcode;
更新:我在这里测试了一些不同的解决方案以提高速度。如果你不期望有很多重复项,那么这个解决方案比那些具有NOT IN (...)
子句的解决方案要好得多,因为它们会在子查询中生成很多行。
如果您重写查询以使用IN (...)
,那么它的执行方式与此处提供的解决方案类似,但SQL代码变得简洁得多。
更新2:如果其中一个键列中有NULL
个值(您真的不应该IMO),那么您可以在该列的条件中使用COALESCE()
,例如
AND COALESCE(T1.col_with_nulls, '[NULL]') = COALESCE(T2.col_with_nulls, '[NULL]')
答案 2 :(得分:19)
在一个完美的世界中,每个表都有一个唯一的标识符。
如果没有任何唯一列(或其组合),请使用the ctid
column:
DELETE FROM tbl
WHERE ctid NOT IN (
SELECT min(ctid) -- ctid is NOT NULL by definition
FROM tbl
GROUP BY name, address, zipcode); -- list columns defining duplicates
上述查询很简单,只列出一次列名。当可以涉及NULL值时,NOT IN (SELECT ...)
是一种棘手的查询样式,但系统列ctid
永远不会为NULL。参见:
将EXISTS
用作demonstrated by @Gordon通常会更快。使用USING
子句like @isapir added later的自联接也是如此。两者都应该导致相同的查询计划。
但请注意重要差异:这些其他查询将 NULL
值视为 不等于 ,GROUP BY
(或DISTINCT
或DISTINCT ON ()
)将NULL值视为相等。如果定义了键列NOT NULL
,则无关紧要。否则,根据您对“重复”的定义,您将需要一种或另一种方法。 或使用IS NOT DISTINCT FROM
比较值(可能无法使用某些索引)。
声明:
ctid
是Postgres的内部实现细节,它不在SQL标准中,可以在没有警告的情况下在主要版本之间进行更改(即使这不太可能)。由于后台进程或并发写操作(但不在同一命令中),其值可能会在命令之间发生变化。
相关:
除了:
DELETE
语句的目标不能是CTE,只能是基础表。这是SQL Server的溢出效应 - 就像你的整个方法一样。
答案 3 :(得分:10)
以下是我提出的问题,使用group by
DELETE FROM mytable
WHERE id NOT in (
SELECT MIN(id)
FROM mytable
GROUP BY name, address, zipcode
)
删除重复项,保留最旧的重复记录。
答案 4 :(得分:6)
我们可以使用窗口函数来非常有效地删除重复的行:
DELETE FROM tab
WHERE id IN (SELECT id
FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), id
FROM tab) x
WHERE x.row_number > 1);
一些PostgreSQL的优化版本(带有ctid):
DELETE FROM tab
WHERE ctid = ANY(ARRAY(SELECT ctid
FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), ctid
FROM tab) x
WHERE x.row_number > 1));
答案 5 :(得分:2)
有效语法在http://www.postgresql.org/docs/current/static/sql-delete.html
指定我会更改您的表以添加唯一的自动递增主键ID,以便您可以运行如下所示的查询,该查询将保留每组重复项中的第一个(即具有最低ID的那一个)。请注意,在Postgres中添加密钥比其他一些DB更复杂一些。
DELETE FROM mytable d USING (
SELECT min(id), name, address, zip
FROM mytable
GROUP BY name, address, zip HAVING COUNT() > 1
) AS k
WHERE d.id <> k.id
AND d.name=k.name
AND d.address=k.address
AND d.zip=k.zip;
答案 6 :(得分:1)
如果您想在表格中保留一行重复的行。
create table some_name_for_new_table as
(select * from (select *,row_number() over (partition by pk_id) row_n from
your_table_name_where_duplicates_are_present) a where row_n = 1);
这将创建一个可以复制的表。
在复制表格之前,请删除列&#39; row_n&#39;
答案 7 :(得分:0)
IRC中的一个常见问题是如何删除在一组列上重复的行,只保留ID最低的行。 此查询对具有相同column1,column2和column3的所有tablename行执行此操作。
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);
有时会使用时间戳字段而不是ID字段。
答案 8 :(得分:0)
如果您想为每一行添加唯一标识符,您只需添加一个(序列号或guid),并将其视为代理键。
CREATE TABLE thenames
( name text not null
, address text not null
, zipcode text not null
);
INSERT INTO thenames(name,address,zipcode) VALUES
('James', 'main street', '123' )
,('James', 'main street', '123' )
,('James', 'void street', '456')
,('Alice', 'union square' , '123')
;
SELECT*FROM thenames;
-- add a surrogate key
ALTER TABLE thenames
ADD COLUMN seq serial NOT NULL PRIMARY KEY
;
SELECT*FROM thenames;
DELETE FROM thenames del
WHERE EXISTS(
SELECT*FROM thenames x
WHERE x.name=del.name
AND x.address=del.address
AND x.zipcode=del.zipcode
AND x.seq < del.seq
);
-- add the unique constrain,so that new dupplicates cannot be created in the future
ALTER TABLE thenames
ADD UNIQUE (name,address,zipcode)
;
SELECT*FROM thenames;
答案 9 :(得分:0)
对于较小的表,我们可以使用rowid伪列来删除重复的行。
您可以在下面使用此查询:
Delete from table1 t1 where t1.rowid > (select min(t2.rowid) from table1 t2 where t1.column = t2.column)