Sql 如何删除没有唯一标识符的重复行
我的表中有重复的行,我想以最有效的方式删除重复的行,因为表很大。经过研究,我提出了以下问题:Sql 如何删除没有唯一标识符的重复行,sql,database,postgresql,duplicates,netezza,Sql,Database,Postgresql,Duplicates,Netezza,我的表中有重复的行,我想以最有效的方式删除重复的行,因为表很大。经过研究,我提出了以下问题: 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 duplicate
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中不起作用。似乎不喜欢WITH子句后面的DELETE?如果没有其他唯一标识符,可以使用ctid:
在每个表中都有一个唯一的、自动递增的id是个好主意。执行这样的删除操作是一个重要原因。下面是我使用group by得出的结论
它删除重复项,保留有重复项的最旧记录。有效语法在中指定 我会修改您的表,添加一个唯一的自动递增主键id,以便您可以运行如下查询,该查询将保留每组重复项中的第一个,即id最低的一个。请注意,在Postgres中添加键比其他一些DBs要复杂一些
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;
我们可以使用窗口功能非常有效地删除重复行:
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);
一些带有ctid的PostgreSQL优化版本:
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));
在一个完美的世界中,每个表都有某种唯一的标识符。
在没有任何唯一列或其组合的情况下,使用:
从tbl中删除
我不在哪里
SELECT minctid-ctid根据定义不为空
来自tbl
按名称、地址、zipcode分组;-列出定义重复项的列
上面的查询很短,只方便地列出一次列名。不在选择中。。。当可能涉及空值时,这是一种复杂的查询样式,但系统列ctid从不为空。见:
使用“存在”通常更快。USING子句的自连接也是如此。两者应产生相同的查询计划
重要区别:这些其他查询将空值视为不相等,而GROUP BY或DISTINCT or将空值视为相等。对于定义为NOTNULL的列不重要。否则,根据您对复制的定义,您将需要一种或另一种方法。或用于比较可能排除某些索引的值
免责声明:
ctid是Postgres的一个实现细节,它不在SQL标准中,可以在没有警告的情况下在主要版本之间进行更改,即使这种情况非常不可能发生。由于后台进程或并发写入操作,其值可以在命令之间更改,但不能在同一命令内更改
相关的:
旁白:
DELETE语句的目标不能是CTE,只能是基础表。这是SQL Server的一种溢出效应——您的整个方法也是如此。来自文档
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字段。如果要在表中的重复行中保留一行,请使用时间戳字段
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);
这将创建一个可以复制的表
复制表之前,请删除列“row\u n”我喜欢@erwin brandstetter的解决方案,但想用USING关键字显示一个解决方案: 更新:为了提高速度,我在这里测试了一些不同的解决方案。如果您不希望有太多的重复项,那么此解决方案的性能要比那些没有重复项的解决方案好得多。。。子句,因为它们在子查询中生成许多行 如果您重写查询以在中使用。。。然后,它的性能与这里介绍的解决方案类似,但SQL代码变得不那么简洁 更新2:如果在其中一个键列中有空值,而您确实不应该这样做,那么您可以在该列的条件中使用COALESCE,例如
AND COALESCE(T1.col_with_nulls, '[NULL]') = COALESCE(T2.col_with_nulls, '[NULL]')
如果您希望每一行都有一个唯一的标识符,那么只需添加一个序列号或guid,并将其视为代理键
对于较小的表,我们可以使用rowid伪列删除重复的行 您可以在下面使用此查询:
从表1 t1中删除,其中t1.rowid>从表1 t2中选择mint2.rowid,其中t1.column=t2。列如果是一次性作业,为什么不在postgresql控制台中运行它?不是一次性作业,而是每周作业,我们总是会得到一些重复值。谢谢为什么会有重复的值?如果你不把它放在第一位呢?重复项是由列名称、地址、zipcode定义的吗?还有其他专栏吗?这些都无关紧要吗?不同的任何列的组合都是唯一的吗?如果某些列在重复项之间存在差异,您希望保留每组中的哪一行?适用于POSTGRESQL也适用于AWS REDSHIFT我的表中没有任何名为ctid的字段。您能解释一下从何处获得此字段吗?thanksctid是一个隐藏字段。检索表定义时,它不会显示。这是一种内部行号。如果不存在行号,将删除不重复的行。嘘
如果存在,请选择1`@GordonLinoff-感谢您的澄清。我知道这是离题的;这就是OT:在我的问题的前缀中的意思;在我的小表格中,我做了:从表格中选择ctid,*。ctid表示为0,1,0,2等,因此我能够对重复的行执行一个简单的delete语句:delete from table,其中ctid='0,1'我的表中没有id,这是netezza数据库。它们没有像sql Server那样的自动递增数字。它是否有另一个唯一标识行的列?HAVING子句是此查询的noise。在任何情况下,每个现有id的计数都大于等于1。你可以删除它。我喜欢这个解决方案,因为它非常简洁。对于我在下面发布的解决方案的性能有什么想法吗?我实际上能够测试它。我有一个大约350k行的表,它有39个重复项,超过7列,没有索引。我首先尝试了按解决方案分组,但耗时超过30秒,因此我将其杀死。然后我尝试了使用解决方案,大约在16秒钟内完成。@isapir:就像我在2014年提到的:notin是方便快捷的语法,但EXISTS更快。与使用USING子句的完全有效查询相同。但有一个微妙的区别。我在上面加了个便条。酷。感谢您的澄清。Erwin的答案更好,因为它正确处理空值,并且不需要在列名中键入两次。正如我在答案开头所写的:我喜欢@Erwin brandstetter的解决方案,但想展示一个解决方案。。。。但是,在发现性能优势后,我更喜欢使用解决方案,尤其是对于大型表。我添加了一个演示如何处理空值的示例。非常好,特别是可以先看一看。为了检查数据列中的空值,我根据表的\dS输出为每列生成了一个T1.col=T2.col或T1.col为空且T2.col为空的条件。现在我可以添加我的主键约束。您可以使用coalesce测试空值,正如我的答案更新2中所希望的。谢谢,这比其他解决方案快得多。我在1小时后放弃了一些版本,这几乎是瞬间完成的Lynetezza不支持主键或唯一键约束,它没有。
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);
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;
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;
AND COALESCE(T1.col_with_nulls, '[NULL]') = COALESCE(T2.col_with_nulls, '[NULL]')
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;