資料表中出現重複列是常見資料品質問題。本文針對三種最典型的重複資料場景,提供經過生產環境驗證的標準解法,並說明各方法的適用條件與效能特性。
需求: Users 表有 u_name、u_pwd,u_name 有重複,需刪除重複名稱,保留第一筆。
DECLARE @id INT = 1, @u_name VARCHAR(50), @u_pwd VARCHAR(50);
CREATE TABLE #new_users (u_name VARCHAR(50), u_pwd VARCHAR(50));
WHILE @id < 1000
BEGIN
IF EXISTS (SELECT u_name FROM Users WHERE u_id = @id)
BEGIN
SELECT @u_name = u_name, @u_pwd = u_pwd FROM Users WHERE u_id = @id;
IF NOT EXISTS (SELECT u_name FROM #new_users WHERE u_name = @u_name)
INSERT INTO #new_users (u_name, u_pwd) VALUES (@u_name, @u_pwd);
END
SET @id += 1;
END
SELECT * FROM #new_users;
-- 後續:重命名原表 → #new_users 改名為 Users
WITH CTE AS (
SELECT
u_name, u_pwd,
ROW_NUMBER() OVER (PARTITION BY u_name ORDER BY u_id) AS rn
FROM Users
)
DELETE FROM CTE WHERE rn > 1;
需求: Users 表有主鍵 id,業務鍵 name 重複,保留每組最小 id。
-- 1. 找出每組保留的 ID SELECT MIN(id) AS id INTO #tmp_keep FROM Users GROUP BY name; -- 2. 依保留 ID 選取完整資料建立新表 SELECT u.* INTO #tmp_data FROM Users u JOIN #tmp_keep k ON u.id = k.id; -- 3. 切換表身分 DROP TABLE Users; EXEC sp_rename '#tmp_data', 'Users'; -- 或 SELECT * INTO Users FROM #tmp_data; DROP TABLE #tmp_data; -- 清理 DROP TABLE #tmp_keep;
SELECT IDENTITY(INT,1,1) AS autoID, * INTO #tmp_with_id FROM Users; -- 之後按 autoID 當主鍵處理
需求: 整列資料完全重複,去重保留單一列。
SELECT DISTINCT * INTO #tmp_unique FROM Users; DROP TABLE Users; SELECT * INTO Users FROM #tmp_unique; DROP TABLE #tmp_unique;
注意:此法會移除所有索引、條件約束、觸發器、權限。執行前請腳本化所有衍生物件,事後重建。
| 場景 | 資料量 | 推薦方法 | 備註 |
|---|---|---|---|
| 無主鍵,依業務鍵去重 | < 10 萬 | ROW_NUMBER() CTE DELETE | 單陳述式、無需重建表 |
| 無主鍵,依業務鍵去重 | > 100 萬 | 分批次 DELETE + 索引支援 | 避免長鎖定、記錄檔暴增 |
| 有主鍵,依業務鍵去重 | 任何 | MIN(ID) 導出新表切換 | 可保留原表結構、索引腳本化重建 |
| 完全重複列 | 任何 | DISTINCT 導出新表 | 最簡潔,但需重建所有衍生物件 |
UNIQUE 條件約束或 PRIMARY KEY。MERGE / INSERT ... WHERE NOT EXISTS 模式。CHECKSUM/HASHBYTES 快速比對。