【问题标题】:Delete duplicate records from a SQL table without a primary key从没有主键的 SQL 表中删除重复记录
【发布时间】:2010-11-02 09:24:26
【问题描述】:

我有下表,其中包含以下记录

create table employee
(
 EmpId number,
 EmpName varchar2(10),
 EmpSSN varchar2(11)
);

insert into employee values(1, 'Jack', '555-55-5555');
insert into employee values (2, 'Joe', '555-56-5555');
insert into employee values (3, 'Fred', '555-57-5555');
insert into employee values (4, 'Mike', '555-58-5555');
insert into employee values (5, 'Cathy', '555-59-5555');
insert into employee values (6, 'Lisa', '555-70-5555');
insert into employee values (1, 'Jack', '555-55-5555');
insert into employee values (4, 'Mike', '555-58-5555');
insert into employee values (5, 'Cathy', '555-59-5555');
insert into employee values (6 ,'Lisa', '555-70-5555');
insert into employee values (5, 'Cathy', '555-59-5555');
insert into employee values (6, 'Lisa', '555-70-5555');

我在这个表中没有任何主键。但是我的表中已经有上述记录。 我想删除 EmpId 和 EmpSSN 字段中具有相同值的重复记录。

例如:Emp id 5

谁能帮我构建一个查询以删除那些重复的记录

提前致谢

【问题讨论】:

  • 你能添加一个主键吗?什么数据库系统或你使用?甲骨文?请在您的问题中说明!
  • 如果 EmpID 和 EmpSSn 相同,但名称不同怎么办?
  • 我们在 SQL Server 中没有任何版本的 varchar2
  • 嗯……“number”和“varchar2”都不是有效的 SQL Server 2005 数据类型……我闻起来像 Oracle。

标签: sql sql-server-2005 tsql duplicate-removal


【解决方案1】:

这很简单。我在 SQL Server 2008 中尝试过

DELETE SUB FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY EmpId, EmpName, EmpSSN ORDER BY EmpId) cnt
 FROM Employee) SUB
WHERE SUB.cnt > 1

【讨论】:

  • 当您有很多列要分组时,这很有效,并且在比较两列时它巧妙地处理了 NULL != NULL。您不必像其他一些答案一样列出每列两次(“a.col = b.col”类型的东西),更重要的是,您不必检查“((a.col = b .col) OR (a.col IS NULL AND b.col IS NULL))" 在 NULL 列上。
  • 这个答案实际上解决了问题,没有结构变化。完美运行。
【解决方案2】:

添加主键(代码如下)

运行正确的删除(代码如下)

考虑一下您为什么不想保留该主键。


假设 MSSQL 或兼容:

ALTER TABLE Employee ADD EmployeeID int identity(1,1) PRIMARY KEY;

WHILE EXISTS (SELECT COUNT(*) FROM Employee GROUP BY EmpID, EmpSSN HAVING COUNT(*) > 1)
BEGIN
    DELETE FROM Employee WHERE EmployeeID IN 
    (
        SELECT MIN(EmployeeID) as [DeleteID]
        FROM Employee
        GROUP BY EmpID, EmpSSN
        HAVING COUNT(*) > 1
    )
END

【讨论】:

  • +1: 引用一些 SQL 大神:“如果它没有主键,它就不是表”
  • +1 主键标识一行。没有PK=没有意义。 @marc_s:聚集索引将表与堆区分开来。没有 PK 就意味着没有数据完整性
  • @gbn: 甚至堆也被认为是一个表 :-) 这句话的意思是:除非你指定一个主键,否则一个表真的没有多大用处(除了在极端情况下比如批量导入/临时表等)
  • 即使在那些边缘情况下,我几乎总是添加一个主键,这样我就可以在需要时删除重复的记录。
  • 看起来正在删除重复项,因此 EmpID 可以是主键。其他数据似乎依赖于它。
【解决方案3】:

使用行号区分重复记录。保留 EmpID/EmpSSN 的第一行号并删除其余的:

    DELETE FROM Employee a
     WHERE ROW_NUMBER() <> ( SELECT MIN( ROW_NUMBER() )
                               FROM Employee b
                              WHERE a.EmpID  = b.EmpID
                                AND a.EmpSSN = b.EmpSSN )

【讨论】:

【解决方案4】:
With duplicates

As
(Select *, ROW_NUMBER() Over (PARTITION by EmpID,EmpSSN Order by EmpID,EmpSSN) as Duplicate From Employee)

delete From duplicates

Where Duplicate > 1 ;

这将更新表格并从表格中删除所有重复项!

【讨论】:

    【解决方案5】:
    select distinct * into newtablename from oldtablename
    

    现在,newtablename 将没有重复记录。

    只需在 sql server 的对象资源管理器中按 F2 即可更改表名(newtablename)。

    【讨论】:

      【解决方案6】:

      代码

      DELETE DUP 
      FROM 
      ( 
          SELECT ROW_NUMBER() OVER (PARTITION BY Clientid ORDER BY Clientid ) AS Val 
          FROM ClientMaster 
      ) DUP 
      WHERE DUP.Val > 1
      

      说明

      使用内部查询构建表视图,其中包含基于Row_Number() 的字段,按您希望唯一的列进行分区。

      从这个内部查询的结果中删除,选择行号不为 1 的任何内容;即重复项;不是原版。

      row_number 窗口函数的order by 子句是有效语法所必需的;您可以在此处输入任何列名。如果您希望更改哪些结果被视为重复(例如,保留最早的或最近的等),那么此处使用的列很重要;即您想指定顺序,以便您希望保留的记录在结果中排​​在第一位。

      【讨论】:

      • 欢迎来到 Stack Overflow!仅代码答案本身并不是很有用。如果您可以添加一些详细信息来解释它如何/为什么回答问题,这将有所帮助。
      • 我很惊讶地得知您可以从别名(或视图)中删除行,并且当您这样做时,相应的行将从底层表!我阅读了更多关于"updatable views" here - “您可以通过视图修改底层基表的数据,只要满足以下条件......”
      【解决方案7】:

      您可以创建一个临时表 #tempemployee,其中包含您的 employee 表的 select distinct。 然后delete from employee。 然后insert into employee select from #tempemployee

      就像 Josh 所说的 - 即使您知道 重复项,删除它们也是不可能的,因为如果某条记录与另一条记录完全重复,则您实际上无法引用它。

      【讨论】:

      • 只有在名称不同但 ID/SSN 匹配的情况下才有技巧。您必须以某种方式选择一个,因为 distinct 对此无济于事。
      • +1 这是最直接和便携的解决方案。 OP 没有说明他使用什么品牌的数据库。
      • @Josh:从 OP 的示例来看,这似乎不是问题。所有列中的重复行都相同。
      【解决方案8】:

      如果不想创建新的主键,可以在 SQL Server 中使用 TOP 命令:

      declare @ID int
      while EXISTS(select count(*) from Employee group by EmpId having count(*)> 1)
      begin
          select top 1 @ID = EmpId
          from Employee 
          group by EmpId
          having count(*) > 1
      
          DELETE TOP(1) FROM Employee WHERE EmpId = @ID
      end
      

      【讨论】:

        【解决方案9】:

        ITS 在查询下方易于使用

        WITH Dups AS
        (
          SELECT col1,col2,col3,
        ROW_NUMBER() OVER(PARTITION BY col1,col2,col3 ORDER BY (SELECT 0)) AS rn
         FROM mytable
        )
        DELETE FROM Dups WHERE rn > 1
        

        【讨论】:

          【解决方案10】:

          delete sub from (select ROW_NUMBER() Over(partition by empid order by empid)cnt from employee)sub 其中 sub.cnt>1

          【讨论】:

          • 欢迎来到stackoverflow。这是一个老问题,有一个很好的答案。如果您认为您的回答增加了一些重要和新的东西,请用更多解释来扩展它。
          【解决方案11】:

          我不是 SQL 专家,所以请多多包涵。我相信你很快就会得到更好的答案。以下是查找重复记录的方法。

          select t1.empid, t1.empssn, count(*)
          from employee as t1 
          inner join employee as t2 on (t1.empid=t2.empid and t1.empssn = t2.empssn)
          group by t1.empid, t1.empssn
          having count(*) > 1
          

          删除它们会更加棘手,因为数据中没有任何内容可以在删除语句中用于区分重复项。我怀疑答案将涉及 row_number() 或添加标识列。

          【讨论】:

            【解决方案12】:
            创建唯一聚集索引 Employee_idx
            关于员工 ( EmpId,EmpSSN )
            用ignore_dup_key

            如果不需要,可以删除索引。

            【讨论】:

              【解决方案13】:

              不需要ID、不需要rowcount()或不需要temp table......

              WHILE 
                (
                   SELECT  COUNT(*) 
                   FROM TBLEMP  
                   WHERE EMPNO 
                          IN (SELECT empno  from tblemp group by empno having count(empno)>1)) > 1 
              
              
              DELETE top(1)  
              FROM TBLEMP 
              WHERE EMPNO IN (SELECT empno  from tblemp group by empno having count(empno)>1)
              

              【讨论】:

                【解决方案14】:

                表 ID 和名称中有两列,其中名称以不同的 ID 重复,因此您可以使用以下查询: . .

                DELETE FROM dbo.tbl1
                WHERE id NOT IN (
                     Select MIN(Id) AS namecount FROM tbl1
                     GROUP BY Name
                )
                

                【讨论】:

                  【解决方案15】:

                  拥有一个没有主键的数据库表确实并且会说非常糟糕的做法......所以在添加一个之后(ALTER TABLE)

                  运行这个直到你看不到任何重复的记录(这就是 HAVING COUNT 的目的)

                  DELETE FROM [TABLE_NAME] WHERE [Id] IN 
                  (
                      SELECT MAX([Id])
                      FROM [TABLE_NAME]
                      GROUP BY [TARGET_COLUMN]
                      HAVING COUNT(*) > 1
                  )
                  
                  
                  SELECT MAX([Id]),[TABLE_NAME], COUNT(*) AS dupeCount
                  FROM [TABLE_NAME]
                  GROUP BY [TABLE_NAME]
                  HAVING COUNT(*) > 1
                  

                  MAX([Id]) 将导致删除最新记录(在首次创建后添加的记录),以防您想要相反的意思,如果需要删除第一条记录并保留插入的最后一条记录,请使用 MIN([Id] )

                  【讨论】:

                    【解决方案16】:
                    select t1.* from employee t1, employee t2 where t1.empid=t2.empid and t1.empname = t2.empname and t1.salary = t2.salary
                    group by t1.empid, t1.empname,t1.salary having count(*) > 1
                    

                    【讨论】:

                    • 它回答了哪个问题?
                    【解决方案17】:

                    delete from employee where rowid in (select rowid from (select rowid, name_count from (select rowid, count(emp_name) as name_count from employee group by emp_id, emp_name) where name_count>1))

                    【讨论】:

                      【解决方案18】:
                      DELETE FROM 'test' 
                      USING 'test' , 'test' as vtable
                      WHERE test.id>vtable.id and test.common_column=vtable.common_column  
                      

                      使用这个我们可以删除重复记录

                      【讨论】:

                        【解决方案19】:
                        ALTER IGNORE TABLE 测试
                                   添加唯一索引'test'('b');
                        

                        @ 这里的 'b' 是唯一性的列名, @ 这里 'test' 是索引名称。

                        【讨论】:

                        • 不是远程有效的 SQL Server 语法。
                        猜你喜欢
                        • 1970-01-01
                        • 2021-11-16
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-03-14
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多