重復記錄治理是數據清洗高頻需求。本文匯總四大類經典方案,涵蓋單/多字段判斷、保留首行/末行、大數據量效能優化。
Name、ID 相同,其他列可能不同。SELECT * FROM People
WHERE PeopleId IN (
SELECT PeopleId FROM People
GROUP BY PeopleId HAVING COUNT(*) > 1
);
DELETE FROM People
WHERE PeopleId IN (
SELECT PeopleId FROM People
GROUP BY PeopleId HAVING COUNT(*) > 1
)
AND RowId NOT IN (
SELECT MIN(RowId) FROM People
GROUP BY PeopleId HAVING COUNT(*) > 1
);
適用:中小表、有 RowId/主鍵標識行順序。
-- 查詢
SELECT * FROM Vitae a
WHERE (a.PeopleId, a.Seq) IN (
SELECT PeopleId, Seq FROM Vitae
GROUP BY PeopleId, Seq HAVING COUNT(*) > 1
);
-- 刪除(保留 RowId 最小)
DELETE FROM Vitae a
WHERE (a.PeopleId, a.Seq) IN (
SELECT PeopleId, Seq FROM Vitae
GROUP BY PeopleId, Seq HAVING COUNT(*) > 1
)
AND a.RowId NOT IN (
SELECT MIN(RowId) FROM Vitae
GROUP BY PeopleId, Seq HAVING COUNT(*) > 1
);
-- 僅統計重復組合
SELECT Name, Sex, COUNT(*)
FROM A
GROUP BY Name, Sex
HAVING COUNT(*) > 1;
SELECT DISTINCT * INTO #Tmp FROM TableName;
DROP TABLE TableName;
SELECT * INTO TableName FROM #Tmp;
DROP TABLE #Tmp;
-- 最後加上唯一索引/主鍵防止再次重復
SELECT IDENTITY(INT,1,1) AS AutoId, * INTO #Tmp FROM TableName;
SELECT MIN(AutoId) AS AutoId INTO #Tmp2 FROM #Tmp GROUP BY Name, Address;
SELECT * FROM #Tmp WHERE AutoId IN (SELECT AutoId FROM #Tmp2);
DECLARE @Max INT, @Id INT;
DECLARE Cur CURSOR LOCAL FAST_FORWARD FOR
SELECT KeyCol, COUNT(*) FROM TableName
GROUP BY KeyCol HAVING COUNT(*) > 1;
OPEN Cur;
FETCH NEXT FROM Cur INTO @Id, @Max;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Max = @Max - 1; -- 保留 1 行
SET ROWCOUNT @Max; -- 限制影響行數
DELETE FROM TableName WHERE KeyCol = @Id;
FETCH NEXT FROM Cur INTO @Id, @Max;
END
CLOSE Cur; DEALLOCATE Cur;
SET ROWCOUNT 0;
優點:避免大事務日志爆漲、鎖升級。缺點:慢、需串行執行。
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY KeyCol ORDER BY Id) AS Rn
FROM TableName
)
DELETE FROM CTE WHERE Rn > 1; -- 保留每組第一行
優勢:單語句、集合操作、可加索引提示、支援多字段 PARTITION BY Col1, Col2。
| 方案 | 數據量 | 優點 | 缺點 |
|---|---|---|---|
| IN + 子查詢 | <10萬 | 寫法簡單 | 子查詢重複掃描、鎖競爭大 |
| ROW_NUMBER CTE | 任意 | 單掃描、並行執行計劃、語法標準 | 需支援窗口函數版本 |
| DISTINCT 重建表 | 完全重復 | 徹底清理 | 需鎖表、丟失身份列/觸發器/權限 |
| 游標循環 | >100萬、無索引 | 可控事務大小 | 極慢、維護難 |
ROW_NUMBER() CTE 刪除,配合 PARTITION BY 多字段去重。DELETE TOP (10000) FROM CTE WHERE Rn > 1 循環直到 @@ROWCOUNT=0。SELECT INTO #Backup 保留重復行供審計。UNIQUE INDEX 或 PRIMARY KEY 從源頭防重。