資料庫因程式邏輯缺失或匯入流程疏漏,常產生重複資料,導致主鍵/唯一索引無法建立、統計報表失真。本文提供三種刪除重複資料的方案,從傳統游標迴圈到現代視窗函數,適用不同版本與場景。
DECLARE @max INT, @id INT;
DECLARE cur_rows CURSOR LOCAL FOR
SELECT 主鍵欄位, COUNT(*)
FROM 表名
GROUP BY 主鍵欄位
HAVING COUNT(*) > 1;
OPEN cur_rows;
FETCH NEXT FROM cur_rows INTO @id, @max;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @max = @max - 1; -- 保留 1 筆
SET ROWCOUNT @max; -- 限制影響列數
DELETE FROM 表名 WHERE 主鍵欄位 = @id;
FETCH NEXT FROM cur_rows INTO @id, @max;
END
CLOSE cur_rows;
DEALLOCATE cur_rows;
SET ROWCOUNT 0; -- 還原無限制
-- 1. 建立無重複暫存表
SELECT DISTINCT * INTO #Tmp FROM tableName;
-- 2. 刪除原表並重建
DROP TABLE tableName;
SELECT * INTO tableName FROM #Tmp;
DROP TABLE #Tmp;
UNIQUE INDEX 或 PRIMARY KEY,從源頭防止重複寫入。-- 1. 加入流水號
SELECT IDENTITY(INT,1,1) AS autoID, * INTO #Tmp FROM tableName;
-- 2. 依業務鍵分組取最小 autoID(即第一筆)
SELECT MIN(autoID) AS autoID INTO #Tmp2
FROM #Tmp
GROUP BY Name, Address; -- 業務鍵欄位
-- 3. 取得去重後結果集
SELECT * FROM #Tmp WHERE autoID IN (SELECT autoID FROM #Tmp2);
-- 實際刪除時可用:
-- DELETE t FROM tableName t
-- WHERE t.autoID NOT IN (SELECT autoID FROM #Tmp2);
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Name, Address ORDER BY ID) AS rn
FROM tableName
)
DELETE FROM CTE WHERE rn > 1;
| 場景 | 推薦方法 | 備註 |
|---|---|---|
| 小量資料、舊版 SQL 2000 | 游標法 | 簡單但慢 |
| 完全重複、可離線維護 | DISTINCT 暫存表 | 速度快,需重建索引/權限 |
| 關鍵欄位重複、線上業務 | CTE + ROW_NUMBER | 單一陳述式、鎖定時間短、可加 WHERE 限制範圍 |
建立完善的資料品質檢核機制(ETL 階段去重、資料庫約束、應用層冪等設計),才是杜絕重複資料的根本之道。