【问题标题】:How find duplicates in a table with no primary key or ID field?如何在没有主键或 ID 字段的表中查找重复项?
【发布时间】:2016-10-18 12:28:52
【问题描述】:

我继承了一个包含重复数据的 SQL Server 数据库。我需要找到并删除重复的行。但是没有 id 字段,我不确定如何找到行。

通常,我会使用 LEFT JOIN 将其与自身进行比较,并检查所有字段是否相同,除了 ID 字段为 table1.id <> table2.id,但没有它,我不知道如何查找重复行和不让它自己也匹配。

表格:

productId int not null,
categoryId int not null,
state varchar(255) not null,
dateDone DATETIME not null

样本数据

1, 3, "started", "2016-06-15 04:23:12.000"
2, 3, "started", "2016-06-15 04:21:12.000"
1, 3, "started", "2016-06-15 04:23:12.000"
1, 3, "done", "2016-06-15 04:23:12.000"

在该示例中,只有第 1 行和第 3 行是重复的。

如何查找重复项?

【问题讨论】:

  • 您可以使用 cte 添加带有分区的 row_number() 然后删除 where row_num > 1 I will work on example
  • @Matt 你能举个例子吗?
  • 一切都在那里,你的测试数据,在删除之前和之后选择。附带说明一下,如果您能够更改架构,您以后可以随时添加标识列。

标签: sql sql-server sql-server-2008 sql-server-2005 sql-server-2012


【解决方案1】:

使用具有(和分组依据)

select 
    productId 
  , categoryId 
  , state
  , dateDone
  , count(*)
from your_table 
group by productId ,categoryId ,state, dateDone
having count(*) >1

【讨论】:

  • 此查询无效。它提供了很多不重复的结果
  • 我已经用 cont(*) 更新了答案。所以你可以检查分组行是否重复..否则。 .. 让我看看一个合适的样本 .. 我检查一下结果是否正确。
【解决方案2】:

您可以使用窗口函数来做到这一点。比如

create table #tmp
   (
        Id INT
   )


insert into #tmp
VALUES (1), (1), (2) --so now we have duplicated rows



WITH CTE AS 
    (
     SELECT 
       ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Id) AS [DuplicateCounter], 
       Id
     FROM #tmp
    )
DELETE FROM CTE
WHERE DuplicateCounter > 1 --duplicated rows have DuplicateCounter > 1

【讨论】:

    【解决方案3】:

    出于某种原因,我以为您想删除它们,我想我读错了,但是只需将我的语句中的 DELETE 切换为 SELECT,现在您就有了所有重复项,而不是原始项。但使用 DELETE 将删除所有重复项,但仍会为您留下 1 条记录,我怀疑这是您想要的。

    IF OBJECT_ID('tempdb..#TT') IS NOT NULL
        BEGIN
            DROP TABLE #TT
        END
    
    CREATE TABLE #TT (
        productId int not null,
        categoryId int not null,
        state varchar(255) not null,
        dateDone DATETIME not null
    )
    
    INSERT INTO #TT (productId, categoryId, state, dateDone)
    VALUES (1, 3, 'started', '2016-06-15 04:23:12.000')
    ,(2, 3, 'started', '2016-06-15 04:21:12.000')
    ,(1, 3, 'started', '2016-06-15 04:23:12.000')
    ,(1, 3, 'done', '2016-06-15 04:23:12.000')
    
    
    SELECT *
    FROM
        #TT
    
    ;WITH cte AS (
        SELECT
           *
           ,RowNum = ROW_NUMBER() OVER (PARTITION BY productId, categoryId, state, dateDone ORDER BY productId) --note what you order by doesn't matter
    
        FROM
    
               #TT
        )
    
    --if you want to delete them just do this otherwise change DELETE TO SELECT
        DELETE
        FROM
            cte
        WHERE
            RowNum > 1
    
        SELECT *
        FROM
            #TT
    

    如果您想要并且可以更改架构,您也可以在事后添加一个标识列,它将填充现有记录

    ALTER TABLE #TT
    ADD Id INTEGER IDENTITY(1,1) NOT NULL
    

    【讨论】:

    • 你有这方面的 SQL 小提琴吗?我无法让它工作
    • 您使用的是什么数据库?您标记了 sql-server、sql-server2008、2012、2005 等。SQL Fiddle 是 MySQL ...我很确定我使用的功能可以追溯到 2005 年,我是 2014 年在 SSMS 中编写的。sqlmag.com/blog/…
    • SQL Fiddle也可以做SQL Server(在左上角可以改)。他们为几个不同的客户提供这些数据库,并且它们都在不同版本的 SQL Server 上。这就是我这样标记它的原因。
    • @DonRhummy 您没有访问 SQL Server SSMS 的权限吗?我刚上 SQLFiddle,甚至他们自己的示例也无法加载 sql-server 2014 或 2008。“SELECT 1 as 'a' 现在甚至不会在 sqlfiddle 上执行。我的脚本在 SQL-server 中工作。2我之后的其他答案使用的是相同的技术,只是没有删除...我会假设您可以访问 SQL 2005 或更高版本的某个地方没有?如果是这样,请将我的代码复制并粘贴到 ssms 并且它应该运行。如果不是,我猜我不知道我为什么要回答这个问题。
    • 我可以访问,但要等到明天。希望在那之前有一些东西可以测试。不用担心。明天做。
    【解决方案4】:

    您可以尝试CTE,然后将实际选择范围从CTE 限制为RN = 1。这是查询:-

    ;WITH ACTE 
    AS 
    (
        SELECT ProductID, categoryID, State, DateDone,
        RN = ROW_NUMBER() OVER(PARTITION BY ProductID, CategoryID, State, DateDone 
                                ORDER BY ProductID, CategoryID, State, DateDone) 
        FROM [Table] 
     ) 
    
    SELECT * FROM ACTE WHERE RN = 1    
    

    【讨论】:

    猜你喜欢
    • 2022-11-18
    • 2018-02-02
    • 2013-04-01
    • 1970-01-01
    • 2019-07-09
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多