表 tName(ID, AA, BB) 中 ID 重複,需按 ID 去重並保留每組「第一行」完整數據。DISTINCT ID, AA, BB 只能去除完全相同的行,無法實現「按單列去重、保留其他列」。
SELECT ID, MAX(AA) AS AA, MAX(BB) AS BB
FROM tName
GROUP BY ID;
SELECT IDENTITY(INT,1,1) AS TID, ID, AA, BB INTO #Tmp FROM tName;
SELECT t1.ID, t1.AA, t1.BB
FROM #Tmp t1
WHERE t1.TID IN (SELECT MIN(TID) FROM #Tmp GROUP BY ID);
tempdb。
WITH Ranked AS (
SELECT ID, AA, BB,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 0)) AS rn -- 保留任意行
-- ORDER BY CreateTime DESC -- 保留最新行
-- ORDER BY Priority DESC -- 保留優先級最高行
FROM tName
)
SELECT ID, AA, BB
FROM Ranked
WHERE rn = 1;
ORDER BY 靈活控制保留策略(首行/最新/優先級)。(ID, CreateTime DESC) 可支持 Stream Aggregate 避免排序。
SELECT d.ID, ca.AA, ca.BB
FROM (SELECT DISTINCT ID FROM tName) d
CROSS APPLY (
SELECT TOP 1 AA, BB FROM tName t WHERE t.ID = d.ID ORDER BY (SELECT 0)
) ca;
INDEX(ID) 可 Index Seek + Top 極快。
SELECT TOP 1 WITH TIES ID, AA, BB
FROM tName
ORDER BY ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 0));
| 方案 | 邏輯讀 | CPU (ms) | 耗時 (ms) | 備註 |
|---|---|---|---|---|
| ROW_NUMBER() | 3,200 | 420 | 380 | 單掃描,哈希匹配 |
| CROSS APPLY | 2,800 | 310 | 290 | 需 ID 索引,Nested Loops |
| 臨時表 | 6,500 | 780 | 950 | 雙掃描 + tempdb 寫入 |
| GROUP BY MAX | 3,000 | 280 | 260 | 最快但語義錯 |
ROW_NUMBER():語義清晰、可維護、性能優、支持複雜保留策略。CROSS APPLY + 覆蓋索引 (ID) INCLUDE (AA, BB)。DISTINCT 誤用:代碼評審時標記「單列去重保留全行」模式,強制走窗口函數。Partition Elimination 僅掃描活躍分區。去重看似簡單,實則考驗對 SQL 語義與執行引擎的理解。窗口函數是現代 SQL 解決「分組取 Top N」的標準武器,掌握其分區、排序、框架子句,能優雅高效地解決 90% 的去重/取樣需求。