如何在第一行之后删除具有相同ID的其余有序行,而该行多次使用该ID?

时间:2019-05-10 07:42:28

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

我对表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和更大的Order

对于LinkID 120

  • Order列对批次进行排序(已在此处排序,但仍然应该这样做)
  • 从顶部开始查找(所以这里Order=1),只要您看不到该值在批处理中出现超过1次就可以了
  • 在第一个重复的Order=3dataunit 1 10也在Order 5)上停止。
  • 删除所有具有LinkID=120 AND Order>=4的内容

LinkID 371(和表中的每个其他LinkID)进行类似的处理之后,处理后的表将如下所示:

RowID   LinkID   Order  Data    DataSpecifier
1       120      1      1       1
2       120      2      1       3
3       120      3      1       10
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
.................................
.................................

我从未做过如此复杂的SQL查询。我知道查询必须是这样的:

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

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


我在这里问了一个非常类似的问题:How to remove rest of the rows with the same ID starting from the first duplicate?

但是,由于我意识到问题中最初的过滤逻辑实际上并不是我所需要的,并且该问题已经得到正确回答,因此我不得不提出这个新问题。

1 个答案:

答案 0 :(得分:2)

在这里,我以前的解决方案已更新。几个GROUP BY就足够了。代码很简单,可以通过阅读来理解。

设置:

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 DuplicatesByLinkID AS
(
    SELECT
        Y.LinkID,
        Y.Data,
        Y.DataSpecifier,
        [Order] = MIN([Order])
    FROM
        #YourData AS Y
    GROUP BY
        Y.LinkID,
        Y.Data,
        Y.DataSpecifier
    HAVING
        COUNT(*) > 1
),
FirstDuplicateByLinkID AS
(
    SELECT
        D.LinkID,
        MinOrder = MIN(D.[Order])
    FROM
        DuplicatesByLinkID AS D
    GROUP BY
        D.LinkID
)
DELETE Y FROM
    #YourData AS Y
    INNER JOIN FirstDuplicateByLinkID AS M ON
        Y.LinkID = M.LinkID AND
        Y.[Order] > M.MinOrder

SELECT * FROM #YourData

结果:

RowID   LinkID  Order   Data    DataSpecifier
1       120     1       1       1
2       120     2       1       3
3       120     3       1       10
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