【问题标题】:How to remove rest of the rows with the same ID starting from the first duplicate?如何从第一个重复项开始删除具有相同 ID 的其余行?
【发布时间】:2019-09-26 05:58:47
【问题描述】:

DataTable 表的结构如下:每一列的数据类型都是 int,RowID 是一个标识列和主键。 LinkID 是一个外键,链接到另一个表的行。

RowID   LinkID   Order  Data    DataSpecifier
1       120      1      1       1
2       120      2      1       3
3       120      3      1       10
4       120      4      1       13
5       120      5      1       10
6       120      6      1       13
7       371      1      6       2
8       371      2      3       5
9       371      3      8       1
10      371      4      10      1
11      371      5      7       2
12      371      6      3       3
13      371      7      7       2
14      371      8      17      4
.................................
.................................

我正在尝试通过以下方式更改每个 LinkID 批次的查询:

  • 使用相同的LinkID 获取每一行(例如,第一批是这里的前 6 行)
  • Order 列排序
  • DataDataSpecifier 列视为一个比较单元(可以将它们视为一列,称为dataunit):
    • Order 1 开始保留尽可能多的行,直到出现重复的dataunit
    • LinkID 的第一个副本开始删除每一行

所以对于LinkID 120

  • 对批次进行排序(此处已排序,但仍应这样做)
  • 从顶部开始查看(此处为Order=1),只要您没有看到重复项即可。
  • 在第一个重复的 Order = 5 处停止(dataunit 1 10 已被看到)。
  • 删除所有具有LinkID=120 AND Order>=5 的内容

在对LinkID371(以及表中的所有其他LinkID)进行类似处理后,处理后的表将如下所示:

RowID   LinkID   Order  Data    DataSpecifier
1       120      1      1       1
2       120      2      1       3
3       120      3      1       10
4       120      4      1       13
7       371      1      6       2
8       371      2      3       5
9       371      3      8       1
10      371      4      10      1
11      371      5      7       2
12      371      6      3       3
.................................
.................................

我已经完成了很多 SQL 查询,但从来没有这么复杂的事情。我知道我需要使用类似这样的查询:

DELETE FROM DataTable  
WHERE RowID IN (SELECT RowID
                FROM DataTable
                WHERE -- ?
                GROUP BY LinkID
                HAVING COUNT(*) > 1 -- ?
                ORDER BY [Order]);

但我似乎无法解决这个问题并正确查询。我最好在纯 SQL 中执行此操作,使用一个可执行(和可重用)查询。

【问题讨论】:

  • 所以将ROW_NUMBER 更改为COUNT @ruohola。然后,不是删除重复项,而是删除计数大于 1 的所有内容。理论是相同的。
  • ROW_NUMBER() OVER (PARTITION BY... -> COUNT({non NULL column}) OVER (PARTITION BY....

标签: sql sql-server tsql duplicates sql-delete


【解决方案1】:

您可以使用ROW_NUMBER() 窗口函数来识别原始行之后的任何行。之后,您可以删除匹配 LinkID 且大于或等于任何遇到的行号大于 1 的 Order 的行。

(我最初使用第二个 CTE 来获取 MIN order,但我意识到只要连接到 order 大于等于任何有第二个实例的 order DataUnitId。通过删除MIN,查询计划变得非常简单和高效。)

WITH DataUnitInstances AS (
  SELECT *
    , ROW_NUMBER() OVER
      (PARTITION BY LinkID, [Data], [DataSpecifier] ORDER BY [Order]) DataUnitInstanceId
  FROM DataTable
)
DELETE FROM DataTable
FROM DataTable dt
INNER JOIN DataUnitInstances dup ON dup.LinkID = dt.LinkID 
  AND dup.[Order] <= dt.[Order]
  AND dup.DataUnitInstanceId > 1

以下是与您想要的结果相匹配的示例数据的输出:

+-------+--------+-------+------+---------------+
| RowID | LinkID | Order | Data | DataSpecifier |
+-------+--------+-------+------+---------------+
| 1     | 120    | 1     | 1    | 1             |
| 2     | 120    | 2     | 1    | 3             |
| 3     | 120    | 3     | 1    | 10            |
| 4     | 120    | 4     | 1    | 13            |
| 7     | 371    | 1     | 6    | 2             |
| 8     | 371    | 2     | 3    | 5             |
| 9     | 371    | 3     | 8    | 1             |
| 10    | 371    | 4     | 10   | 1             |
| 11    | 371    | 5     | 7    | 2             |
| 12    | 371    | 6     | 3    | 3             |
+-------+--------+-------+------+---------------+

【讨论】:

  • 感谢您的好回答 :) 使用我的实际数据库表和列名进行修改非常容易,而且效果很好!
【解决方案2】:

我们可以在这里尝试使用 CTE 来简化操作:

WITH cte AS (
    SELECT *,
        COUNT(*) OVER (PARTITION BY LinkID, Data, DataSpecifier ORDER BY [Order]) - 1 cnt
    FROM DataTable
),
cte2 AS (
    SELECT *,
        SUM(cnt) OVER (PARTITION BY LinkID ORDER BY [Order]) num
    FROM cte
)

DELETE
FROM cte
WHERE num > 0;

这里的逻辑是使用COUNT作为解析函数来识别重复记录。我们使用LinkID 以及DataDataSpecifier 的分区。任何Order 值大于或等于第一个非零计数记录的记录都将被删除。

这是一个演示,证明CTE的逻辑是正确的:

Demo

【讨论】:

    【解决方案3】:

    此解决方案使用APPLY 来查找每个链接的最小订单。

    设置:

    IF OBJECT_ID('tempdb..#YourData') IS NOT NULL
        DROP TABLE #YourData
    
    CREATE TABLE #YourData (
        RowID INT,
        LinkID INT,
        [Order] INT,
        Data INT,
        DataSpecifier INT)
    
    INSERT INTO #YourData (
        RowID,
        LinkID,
        [Order],
        Data,
        DataSpecifier)
    VALUES
        ('1', ' 120', '1', '1', ' 1'), 
        ('2', ' 120', '2', '1', ' 3'), 
        ('3', ' 120', '3', '1', ' 10'), 
        ('4', ' 120', '4', '1', ' 13'), 
        ('5', ' 120', '5', '1', ' 10'), 
        ('6', ' 120', '6', '1', ' 13'), 
        ('7', ' 371', '1', '6', ' 2'), 
        ('8', ' 371', '2', '3', ' 5'), 
        ('9', ' 371', '3', '8', ' 1'), 
        ('10', '371', '4', '10', '1'), 
        ('11', '371', '5', '7', ' 2'), 
        ('12', '371', '6', '3', ' 3'), 
        ('13', '371', '7', '7', ' 2'), 
        ('14', '371', '8', '17', '4')
    

    解决方案:

    ;WITH MinOrderToDeleteByLinkID AS
    (
        SELECT
            T.LinkID,
            MinOrder = MIN(C.[Order])
        FROM
            #YourData AS T
            OUTER APPLY (
                SELECT TOP 1
                    C.*
                FROM
                    #YourData AS C
                WHERE
                    C.LinkID = T.LinkID AND
                    C.Data = T.Data AND
                    C.DataSpecifier = T.DataSpecifier AND
                    C.[Order] > T.[Order]
                ORDER BY
                    T.[Order]) AS C
        GROUP BY
            T.LinkID
    )
    DELETE Y FROM
    -- SELECT Y.* FROM
        #YourData AS Y
        INNER JOIN MinOrderToDeleteByLinkID AS M ON
            Y.LinkID = M.LinkID AND
            Y.[Order] >= M.MinOrder
    

    要删除的行如下:

    RowID   LinkID  Order   Data    DataSpecifier
    5       120     5       1       10
    6       120     6       1       13
    13      371     7       7       2
    14      371     8       17      4
    

    ...对应于元组Data-DataSpecified 开始重复特定LinkID 的点。

    【讨论】:

      猜你喜欢
      • 2019-09-28
      • 1970-01-01
      • 2015-11-30
      • 1970-01-01
      • 2020-12-29
      • 2016-01-28
      • 2013-09-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多