萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

SQL 刪除重複數據多種方法對比與最佳實踐

場景定義

假設表 tablename 中 name, address 重複,需保留一條唯一記錄。

方法一:臨時表 + 自增 ID 去重(通用,適合無主鍵表)

-- 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;

缺點:游標效率低,不推薦大數據量使用。

方法三:DISTINCT 重建表(簡單粗暴,鎖表久)

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.時間
);

現代 MySQL (8.0+) 推薦寫法:CTE + ROW_NUMBER()

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_NUMBERMySQL 8.0+ 生產環境首選高低
copyright © 萬盛學電腦網 all rights reserved