資料表因缺乏唯一約束或 ETL 重複導入常產生重複行。本文對比 Oracle ROWID、標準 SQL GROUP BY、DISTINCT 三種去重策略,提供查詢與刪除完整語句,並分析適用場景與效能差異。
利用 Oracle 物理地址 ROWID` 唯一標識每行,保留每組最大 ROWID`(通常為最新插入)。
SELECT *
FROM table1 a
WHERE rowid != (
SELECT MAX(rowid)
FROM table1 b
WHERE a.name1 = b.name1
AND a.name2 = b.name2 -- 所有業務主鍵列
);
DELETE FROM table1 a
WHERE rowid != (
SELECT MAX(rowid)
FROM table1 b
WHERE a.name1 = b.name1
AND a.name2 = b.name2
);
✅ 優點:精確保留一行,無需臨時表。
❌ 缺點:Oracle 專有,跨庫遷移需改寫;子查詢大表時效能一般。
SELECT COUNT(*), MAX(name)
FROM student
GROUP BY num
HAVING COUNT(*) > 1;
DELETE FROM student
WHERE num IN (
SELECT num FROM student
GROUP BY num
HAVING COUNT(*) > 1
);
⚠️ 警告:上述語句會把重複組的所有行全部刪除,而非保留一行。生產環境嚴禁直接執行。
DELETE FROM student
WHERE rowid NOT IN (
SELECT MIN(rowid) -- 或 MAX(rowid),視保留策略
FROM student
GROUP BY num
);
MySQL/PostgreSQL 替換 rowid 為主鍵 id:
DELETE t1 FROM student t1
JOIN (
SELECT MIN(id) AS keep_id FROM student GROUP BY num
) t2 ON t1.num = t2.keep_id
WHERE t1.id != t2.keep_id;
-- 1. 建去重新表
CREATE TABLE table_new AS
SELECT DISTINCT * FROM table1;
-- 2. 清空原表
TRUNCATE TABLE table1;
-- 3. 寫回去重數據
INSERT INTO table1 SELECT * FROM table_new;
-- 4. 刪除臨時表
DROP TABLE table_new;
✅ 優點:語法簡單,跨資料庫通用。
❌ 缺點:大表鎖表時間長、需雙倍空間、丟失自增 ID 連續性、外鍵關聯斷裂風險。
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY biz_key ORDER BY create_time DESC) AS rn
FROM big_table
)
DELETE FROM big_table
WHERE id IN (SELECT id FROM ranked WHERE rn > 1);
LIMIT 10000 分批刪除,避免長事務與鎖升級。ALTER TABLE t ADD UNIQUE uk_biz (biz_col1, biz_col2);