首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL 查詢重複值記錄的兩種寫法與效能大比拼

     編輯:關於SqlServer

資料庫去重實戰:如何高效找出重複記錄

在數據清洗、主鍵建立、業務合規檢查等場景中,「查詢某欄位重複值的記錄」是高頻需求。本文以學生表(student)為例,對比兩種經典寫法的執行計畫與實測效能,並給出生產環境推薦方案。

場景設定

  • 表名:student
  • 欄位:Sno(學號,擬設為主鍵)、ID(身份證,唯一業務鍵)
  • 數據量:10,000+ 行,Sno 存在重複
  • 目標:找出所有 Sno 重複的完整記錄

方法一:自連接 + 不等值條件(推薦,秒級完成)

SELECT *
FROM student AS A
WHERE Sno IN (
    SELECT Sno 
    FROM student AS B 
    WHERE A.ID <> B.ID
);

原理解析

  • 外層遍歷表 A,內層子查詢在表 B 中尋找「同學號但不同身份證」的記錄。
  • A.ID <> B.ID 確保不匹配自身,等價於「存在至少另一筆相同 Sno」。
  • 優化器易轉換為 Hash Match / Merge Join,配合 Sno 索引可達線性掃描複雜度。

方法二:相關子查詢 + COUNT(*)(不推薦,分鐘級甚至卡死)

SELECT *
FROM student AS A
WHERE (SELECT COUNT(*) FROM student WHERE Sno = A.Sno) > 1;

效能陷阱

  • 對外層每一行(10,000+ 次)都執行一次全表掃描/索引查找計算 COUNT(*)。
  • 屬於典型的 RBAR (Row-By-Agonizing-Row) 模式,複雜度接近 O(N²)。
  • 大表時會導致 CPU 飆升、鎖升級、甚至實例假死。

實測對比(10,000 行測試數據)

指標方法一 (IN + 自連接)方法二 (COUNT 子查詢)
執行時間~2 秒~4 分 30 秒
邏輯讀取~30,000 頁~1.2 億頁
CPU 時間~150 ms~180,000 ms
執行計畫Hash Match (Right Semi Join)Nested Loops + Table Spool

更優寫法:窗口函數(SQL Server 2005+ / PostgreSQL / Oracle / MySQL 8.0+)

WITH DupCTE AS (
    SELECT 
        *,
        COUNT(*) OVER (PARTITION BY Sno) AS Cnt
    FROM student
)
SELECT * FROM DupCTE WHERE Cnt > 1;
  • 單次掃描完成分組計數,無需自連接或子查詢。
  • 執行計畫通常為 Segment + Sequence Project + Filter,效能與方法一持平甚至更優。
  • 代碼可讀性最強,易維護。

進階場景:保留一條刪除其餘重複

-- 保留每組 Sno 的最小 ID,刪除其餘
WITH Ranked AS (
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY Sno ORDER BY ID) AS RN
    FROM student
)
DELETE FROM student
WHERE ID IN (SELECT ID FROM Ranked WHERE RN > 1);

索引建議

-- 針對去重查詢建立覆蓋索引
CREATE INDEX IX_student_Sno_ID ON student (Sno) INCLUDE (ID, 其他常用列);
  • 方法一、窗口函數皆可直接利用此索引進行 ORDER BY Sno 流式聚合。
  • 避免在 WHERE 中對 Sno 做函數運算(如 LTRIM(RTRIM(Sno))),導致索引失效。

總結速查

寫法適用版本效能推薦等級
IN + 自連接 (<>)所有版本⭐⭐⭐⭐⭐✅ 生產首選
窗口函數 COUNT(*) OVERSQL Server 2005+ / PG / MySQL 8+⭐⭐⭐⭐⭐✅ 現代首選
EXISTS + 子查詢所有版本⭐⭐⭐⭐✅ 可選
GROUP BY HAVING COUNT(*) > 1 (再 JOIN 回表)所有版本⭐⭐⭐⚠️ 需二次掃描
相關子查詢 COUNT(*)所有版本⭐ (極差)❌ 禁用

下次面對「找重複」需求,請優先選擇 窗口函數 或 IN + 自連接,並配合適當索引,讓百萬級數據也能秒級出結果。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved