我对表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
列对其进行排序Data
和DataSpecifier
列视为一个比较单元(可以将它们视为一列,称为dataunit
):
Order
1开始保留尽可能多的行,直到出现重复的dataunit
LinkID
的第一个重复项之后的每一行对于LinkID
120
:
Order=1
),只要没有重复就可以找到。Order = 5
(已经看到dataunit
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
.................................
.................................
我已经做了很多SQL查询,但是从来没有这么复杂的东西。我知道我需要使用类似这样的查询:
DELETE FROM DataTable
WHERE RowID IN (SELECT RowID
FROM DataTable
WHERE -- ?
GROUP BY LinkID
HAVING COUNT(*) > 1 -- ?
ORDER BY [Order]);
但是我似乎无法解决这个问题并使查询正确。我最好在纯SQL中执行一个可执行(可重用)查询。
答案 0 :(得分: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
与Data
和DataSpecifier
一起使用。 Order
值大于或等于第一个非零计数的记录的任何记录都将被删除。
这是一个演示CTE逻辑正确的演示:
答案 1 :(得分:1)
您可以使用ROW_NUMBER()
窗口函数来识别原始行之后的任何行。之后,您可以删除具有匹配的LinkID
且大于或等于任何遇到的Order
且行号大于1的行。
(我最初使用第二个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 :(得分:1)
此解决方案使用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
开始重复的点。