如何从第一个重复项开始删除具有相同ID的其余行?

ruo*_*ola 5 sql t-sql sql-server 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
.................................
.................................
Run Code Online (Sandbox Code Playgroud)

我正在尝试执行一个查询,该查询LinkID以以下方式更改每个批次:

  • 每行都一样LinkID(例如,第一批是这里的前6行)
  • Order列排序
  • DataDataSpecifier列视为一个比较单位(可以将它们视为一列,称为dataunit):
    • Order1开始保留尽可能多的行,直到dataunit通过
    • 从该第一个重复项开始删除该行的每一行 LinkID

所以对于LinkID 120

  • 对批次进行排序(已在此处排序,但仍应执行)
  • 从顶部开始寻找(所以Order=1在这里),只要没有发现重复即可。
  • 停在第一个重复项Order = 5dataunit 1 10已经看到)。
  • 删除所有具有 LinkID=120 AND Order>=5

经过类似的过程LinkID 371(以及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
.................................
.................................
Run Code Online (Sandbox Code Playgroud)

我已经做了很多SQL查询,但是从来没有这么复杂。我知道我需要使用类似以下的查询:

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

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

Dan*_*nez 1

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

(我最初使用第二个 CTE 来获取MIN order,但我意识到只要连接order大于等于orderDataUnitId 的第二个实例,就没有必要。通过删除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
Run Code Online (Sandbox Code Playgroud)

以下是示例数据的输出,与您期望的结果相匹配:

+-------+--------+-------+------+---------------+
| 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             |
+-------+--------+-------+------+---------------+
Run Code Online (Sandbox Code Playgroud)