仅保留SQL Server中的一个重复行

时间:2015-06-17 21:21:38

标签: sql sql-server duplicate-removal

表格有一些重复的记录。这是一个示例:

create table TestStage
(
     primKey int,
     name varchar(50),
     Flag varchar(10)
)

INSERT INTO TestStage VALUES(12,'DDD','I')
INSERT INTO TestStage VALUES(15,'EEE','N')
INSERT INTO TestStage VALUES(12,'AAA','I')
INSERT INTO TestStage VALUES(16,'MMM','N')

primKey是基于此列的记录被定义为重复或不重复的列。因此在上面的例子中,第1行和第3行是重复的。我需要删除第1行并保留第3行。

我在其他一些帖子中使用的CTE如下:

WITH cte AS 
(
  SELECT 
      ROW_NUMBER() OVER(PARTITION BY primKey ORDER BY primKey) AS [rn]
  FROM 
      TestStage
)
DELETE cte 
WHERE [rn] > 1

但这显然会删除第3行而不是第1行。

我该如何处理?有几点需要注意......

  1. 这是一个示例表。实际表中的重复记录,由不一定是数字的键组合确定。

  2. 我现在无法为表创建标识列。

  3. 该表在键上有一个索引,用于确定它是否重复(在此示例中为primKey)是否可以使用。

  4. 感谢。

3 个答案:

答案 0 :(得分:2)

可能你的误解是你认为表有一些内在的顺序。没有桌子有订单。查询必须建立订单。您需要定义一些有意义的ORDER BY子句来确定要删除的行。

如果您依赖“桌面订单”并且没有其他方法可以选择行,那么您无能为力。

现在你的ORDER BY primKey事物完全没有效果,因为在每个分区中,所有primKey值都是相同的。这与ORDER BY (SELECT NULL)相同。

  

要保留的分区的行号的最高值

没有内在的行号。你需要接受这个事实。

答案 1 :(得分:0)

以下是70-461培训套件的文字:

  

似乎输出按empid排序,但事实并非如此   保证。更令人困惑的是,如果你运行查询   反复地,似乎结果一直在返回   订购;但同样,这不能保证。当数据库引擎时(SQL   在这种情况下服务器)处理此查询,它知道它可以返回   任何顺序的数据都是因为没有明确的指令   以特定顺序返回数据。可能是因为   优化和其他原因,SQL Server数据库引擎选择   这次以特定方式处理数据。甚至还有一些   如果是物理的话,这种选择会重复的可能性   情况保持不变。但是两者之间存在很大差异   由于优化和其他原因而可能发生的事情   什么是实际保证。

     

数据库引擎可能 - 有时也可能   确实可以影响行所在的选择   知道可以自由地回来了。这种变化的例子   在选择中包括数据分布的变化,可用性   物理结构,如索引和资源的可用性   像CPU和内存。此外,随着发动机的变化   升级到更新版本的产品,甚至是申请后   一个服务包,优化方面可能会改变。反过来,这样   除其他外,更改可能会影响行的顺序   结果。

     

简而言之,这不足以强调:一个查询   没有明确的指示来返回a中的行   特定顺序不保证结果中的行顺序。   查询的子句,这是下一节的重点。

答案 2 :(得分:-1)

你可以试试这个......它"可能"保留表中当前订单的任何内容。

;
WITH    cte
AS (SELECT
      primKey,
      name,
      ROW_NUMBER() OVER (PARTITION BY primKey ORDER BY primKey) AS [rn]
    FROM
      TestStage
    )
DELETE
    t
FROM
    TestStage t
    JOIN cte ON t.primKey = cte.primKey
                AND t.name = cte.NAME
WHERE
    cte.rn < (SELECT MAX (rn) FROM cte WHERE primKey = t.primKey)

此处有一项测试,您可以查看是否可以更改订单。计算出订单中断所需的次数。然后弄清楚它是否值得冒险。

DECLARE @TestStage TABLE(
    primKey int,
    name varchar(50),
    Flag varchar(10)
)

INSERT INTO @TestStage VALUES
(12,'DDD','I'),(15,'EEE','N'),(12,'AAA','I'),(16,'MMM','N')

DECLARE @TestStageOrder TABLE(
    primKey int,
    name varchar(50),
    Flag varchar(10),
    [order] int
)

DECLARE @TestCount INT = 0
WHILE @TestCount < 100000
BEGIN
    INSERT INTO @TestStageOrder
    SELECT *, ROW_NUMBER() OVER (PARTITION BY primKey ORDER BY primKey)
    FROM @TestStage
    SET @TestCount = @TestCount + 1
END

SELECT  primKey, name, Flag, [order], COUNT(*) 
FROM    @TestStageOrder
GROUP BY primKey, name, Flag, [order]