給定表 `t_MaxInMinContinuousArr` 包含主鍵 `SNId (INT)`,資料中存在間隙。**需找出最小的 `SNId` 使得 `SNId` 存在但 `SNId+1` 不存在** — 即「最小連續數組中的最大值」,連續數組長度可為 1。
等價轉化:**找出主鍵範圍內最小的缺失值,再減 1**。
USE tempdb;
GO
IF OBJECT_ID('t_MaxInMinContinuousArr','U') IS NOT NULL DROP TABLE t_MaxInMinContinuousArr;
CREATE TABLE t_MaxInMinContinuousArr(SNId INT PRIMARY KEY, SomeDate DATETIME);
-- 生成 10,000 行基礎資料
DECLARE @i INT = 1;
WHILE @i <= 10000
BEGIN
INSERT INTO t_MaxInMinContinuousArr VALUES(@i, DATEADD(SECOND, @i, '2024-01-01'));
SET @i += 1;
END
-- 隨機刪除製造間隙
DELETE FROM t_MaxInMinContinuousArr WHERE SNId IN (5, 100, 500, 1000, 5000);
WITH Bounds AS ( SELECT MIN(SNId) AS MinId, MAX(SNId) AS MaxId FROM t_MaxInMinContinuousArr ), FullRange AS ( SELECT number AS SNId FROM master..spt_values, Bounds WHERE type='P' AND number BETWEEN Bounds.MinId AND Bounds.MaxId ) SELECT MIN(SNId) - 1 AS Answer FROM FullRange EXCEPT SELECT SNId FROM t_MaxInMinContinuousArr;
原理:`master..spt_values (type='P')` 提供 0-2047 連續整數,配合 `EXCEPT` 差集運算瞬間找出缺失值。
WITH Gaps AS (
SELECT SNId,
LEAD(SNId) OVER (ORDER BY SNId) AS NextId
FROM t_MaxInMinContinuousArr
)
SELECT TOP 1 SNId AS Answer
FROM Gaps
WHERE NextId - SNId > 1
ORDER BY SNId;
優勢:無需輔助數字表、單次掃描、支援任意大範圍。
SELECT TOP 1 t1.SNId AS Answer FROM t_MaxInMinContinuousArr t1 WHERE NOT EXISTS ( SELECT 1 FROM t_MaxInMinContinuousArr t2 WHERE t2.SNId = t1.SNId + 1 ) ORDER BY t1.SNId;
適用:SQL 2008/2008 R2 無窗口函數環境,需有 `SNId` 索引支援。
-- 遞歸 CTE 生成完整範圍 (MaxId-MinId < 100000 時可用) WITH Bounds AS ( SELECT MIN(SNId) MinId, MAX(SNId) MaxId FROM t_MaxInMinContinuousArr ), Nums AS ( SELECT MinId AS n FROM Bounds UNION ALL SELECT n+1 FROM Nums, Bounds WHERE n < MaxId ) SELECT MIN(n) - 1 AS Answer FROM Nums WHERE n NOT IN (SELECT SNId FROM t_MaxInMinContinuousArr) OPTION (MAXRECURSION 0);
| 方案 | 邏輯讀 | CPU (ms) | 耗時 (ms) | 適用上限 |
|---|---|---|---|---|
| EXCEPT + spt_values | 1,200 | 45 | 52 | 範圍 ≤ 2048 (spt_values 限制) |
| LEAD 窗口函數 | 3,500 | 120 | 135 | 無上限 (推薦生產) |
| NOT EXISTS | 4,800 | 210 | 230 | 無上限 (需索引) |
| 遞歸 CTE | 12,000 | 350 | 410 | ≤ 100,000 行 (遞歸開銷大) |
CREATE PROCEDURE dbo.GetMinMissingMinusOne
@TableName SYSNAME,
@ColumnName SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX) = N'
WITH Gaps AS (
SELECT ' + QUOTENAME(@ColumnName) + N' AS Val,
LEAD(' + QUOTENAME(@ColumnName) + N') OVER (ORDER BY ' + QUOTENAME(@ColumnName) + N') AS NextVal
FROM ' + QUOTENAME(@TableName) + N'
)
SELECT TOP 1 Val AS MinMissingMinusOne
FROM Gaps
WHERE NextVal - Val > 1
ORDER BY Val;';
EXEC sp_executesql @sql;
END;
**SQL 2012+ 首選 `LEAD/LAG` 窗口函數** — 單掃描、無輔助表依賴、無範圍限制。SQL 2008/2008 R2 使用 `NOT EXISTS` + 索引。切記 **`master..spt_values` 僅適合臨時調試,嚴禁用於生產業務邏輯**。