-- 查詢
SELECT * FROM People
WHERE PeopleID IN (SELECT PeopleID FROM People GROUP BY PeopleID HAVING COUNT(*) > 1);
-- 刪除(保留最小 RowID)
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);
-- 查詢
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
);
注意:某些資料庫不支援 WHERE (col1, col2) IN (...) 語法,需改用 EXISTS 或 JOIN。
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY PeopleID, Seq ORDER BY RowID) AS RN
FROM Vitae
)
DELETE FROM Vitae
WHERE RowID IN (SELECT RowID FROM CTE WHERE RN > 1);
ORDER BY RowID ASC 保留最早,DESC 保留最新DECLARE @Max INT, @ID INT;
DECLARE cur CURSOR LOCAL FOR
SELECT MainField, COUNT(*) FROM TableName GROUP BY MainField HAVING COUNT(*) > 1;
OPEN cur;
FETCH NEXT FROM cur INTO @ID, @Max;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Max = @Max - 1;
SET ROWCOUNT @Max; -- 只影響後續單一陳述式
DELETE FROM TableName WHERE MainField = @ID;
FETCH NEXT FROM cur INTO @ID, @Max;
END
CLOSE cur; DEALLOCATE cur;
SET ROWCOUNT 0;
缺點:逐組執行 DELETE,大量資料極慢;SET ROWCOUNT 已棄用(SQL 2012+),建議改用 TOP (@Max)。
SELECT DISTINCT * INTO #Tmp FROM TableName;
DROP TABLE TableName;
SELECT * INTO TableName FROM #Tmp;
DROP TABLE #Tmp;
或加上唯一索引防止再犯:ALTER TABLE TableName ADD CONSTRAINT UQ_Key UNIQUE (Col1, Col2...)
| 方案 | 適用版本 | 效能 | 彈性 | 建議場景 |
|---|---|---|---|---|
| 子查詢 IN | 所有 | 中 | 低 | 小表、單欄位、一次性清理 |
| ROW_NUMBER CTE | 2005+ | 高 | 高 | 生產環境首選、多欄位、保留策略靈活 |
| 游標 | 所有 | 極低 | 中 | 極特殊邏輯、無視窗函數支援 |
| DISTINCT 重建 | 所有 | 高 | 低 | 完全重複、表可重建、無外鍵 |
DBCC CHECKIDENT / 索引重建