假設表 tablename 中 name, address 重複,需保留一條唯一記錄。
-- 1. 複製數據到臨時表並加自增 ID
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. 刪除不在保留集合中的行
DELETE FROM tablename WHERE autoid NOT IN (SELECT autoid FROM #tmp2);
優點:邏輯清晰,不依賴主鍵。缺點:需臨時表,大表性能一般。
DECLARE @max INT, @id INT;
DECLARE cur CURSOR LOCAL FOR
SELECT 主鍵, COUNT(*) FROM 表名 GROUP BY 主鍵 HAVING COUNT(*) > 1;
OPEN cur;
FETCH cur INTO @id, @max;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @max = @max - 1;
SET ROWCOUNT @max;
DELETE FROM 表名 WHERE 主鍵 = @id;
FETCH cur INTO @id, @max;
END
CLOSE cur;
SET ROWCOUNT 0;
缺點:游標效率低,不推薦大數據量使用。
SELECT DISTINCT * INTO #tmp FROM tablename;
DROP TABLE tablename;
SELECT * INTO tablename FROM #tmp;
DROP TABLE #tmp;
風險:丟失索引、約束、權限等元數據;大表鎖定時間長,生產環境慎用。
例:消費記錄表,同 id、機器、類型 且時間間隔 <2 分鐘視為重複,保留最早一條。
-- 先查看將刪除的數據
SELECT * FROM 表 a
WHERE EXISTS (
SELECT 1 FROM 表
WHERE 消費類型 = a.消費類型
AND 時間 >= DATEADD(minute, -2, a.時間)
AND 時間 < a.時間
);
-- 確認無誤後執行刪除
DELETE a FROM 表 a
WHERE EXISTS (
SELECT 1 FROM 表
WHERE 消費類型 = a.消費類型
AND 時間 >= DATEADD(minute, -2, a.時間)
AND 時間 < a.時間
);
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY name, address ORDER BY id) AS rn
FROM tablename
)
DELETE FROM tablename
WHERE id IN (SELECT id FROM ranked WHERE rn > 1);
優勢:標準 SQL、可讀性強、性能優、保留主鍵/索引完整。
| 方法 | 適用場景 | 性能 | 風險 |
|---|---|---|---|
| 臨時表+自增ID | 無主鍵、中小表 | 中 | 低 |
| 游標循環 | 極少量重複、需精細控制 | 低 | 中 |
| DISTINCT重建 | 開發測試、小表 | 中 | 高(元數據丟失) |
| 業務鍵條件刪除 | 有明確業務去重規則 | 高 | 低 |
| CTE+ROW_NUMBER | MySQL 8.0+ 生產環境首選 | 高 | 低 |