SQL Server:查找缺失 ID 的有效方法
Posted
技术标签:
【中文标题】SQL Server:查找缺失 ID 的有效方法【英文标题】:SQL Server : efficient way to find missing Ids 【发布时间】:2019-03-09 16:22:18 【问题描述】:我正在使用 SQL Server 存储数千万条记录。我需要能够查询其表以查找 Id 列中存在间隙的缺失行,因为应该没有。
我目前正在使用我在 *** 上找到的解决方案:
CREATE PROCEDURE [dbo].[find_missing_ids]
@Table NVARCHAR(128)
AS
BEGIN
DECLARE @query NVARCHAR(MAX)
SET @query = 'WITH Missing (missnum, maxid) '
+ N'AS '
+ N'('
+ N' SELECT 1 AS missnum, (select max(Id) from ' + @Table + ') '
+ N' UNION ALL '
+ N' SELECT missnum + 1, maxid FROM Missing '
+ N' WHERE missnum < maxid '
+ N') '
+ N'SELECT missnum '
+ N'FROM Missing '
+ N'LEFT OUTER JOIN ' + @Table + ' tt on tt.Id = Missing.missnum '
+ N'WHERE tt.Id is NULL '
+ N'OPTION (MAXRECURSION 0);';
EXEC sp_executesql @query
END;
此解决方案一直运行良好,但随着表的增长,它变得越来越慢且占用更多资源。现在,在 3800 万行的表上运行该过程大约需要 3.5 分钟和大量 CPU。
有没有更有效的方法来执行此操作?在发现某个范围不包含任何缺失的 Id 后,我不再需要再次检查该范围。
【问题讨论】:
我将使用与递归 cte 不同的方法生成计数表,如下所示:***.com/a/1394239/5070879。第二件事 ID 可能对 IDENTITY/SEQUENCE 都有差距 看看这个answer 寻找运行计数器中的空白。 我建议使用QUOTENAME
,你那里的东西很容易注入
真正的问题是你为什么关心 id 列中的空白。
【参考方案1】:
JBJ 的回答差不多了。查询需要返回每个缺失值范围的 From 和 Through。
select B+1 as [From],A-1 as[Through]from
(select StuffID as A,
lag(StuffID)over(order by StuffID)as B from Stuff)z
where A<>B+1
order by A
我创建了一个包含 5000 万条记录的测试表,然后删除了一些。结果的第一行是:
From Through
33 35
这表示 33 到 35 范围内的所有 ID 都缺失,即 33、34 和 35。
在我的机器上查询耗时 37 秒。
【讨论】:
【参考方案2】:试试
select pId
from (select Id, lag(Id) over (order by Id) pId from yourschema.yourtable) e
where pId <> (Id-1)
order by Id
用适当的表信息替换 yourschema.yourtable
【讨论】:
这将如何处理多个值的间隙,例如1, 2, 5, 13
?
下一个选项是循环遍历每个 id,如果没有找到将其放入表中,但使用新 id 添加新记录将需要设置 identity_insert 关闭/打开,这不是最好的方法。有 2 种可能的数据类型可用于新的 Id 列,bigint 从低开始(身份(-9223372036854775808,1)非空主键)或唯一标识符默认(newid())非空主键。两者都需要在添加到表之前删除 PK 索引,并且都会对依赖旧键的所有表造成严重破坏。【参考方案3】:
试试这个解决方案,它会比CTE
更快。
;WITH CTE AS
(
SELECT ROW_NUMBER()
OVER (
ORDER BY (SELECT NULL)) RN
FROM ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) v(id) --10 ROWS
CROSS JOIN ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) v1(id)--100 ROWS
CROSS JOIN ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) v2(id) --1000 ROWS
CROSS JOIN ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) v3(id) --10000 ROWS
CROSS JOIN ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) v4(id)--100000 ROWS
CROSS JOIN ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) v5(id)--1000000 ROWS
)
SELECT RN AS Missing
FROM CTE C
LEFT JOIN YOURABLE T ON T.ID=R.ID
WHERE T.ID IS NULL
如果你愿意,你也可以使用master..[spt_values]
来生成如下数字。
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) RN
FROM master..[spt_values] T1
CROSS JOIN (select top 500 * from master..[spt_values]) T2
以上查询将生成1268500
数字
注意:您需要根据您的要求添加CROSS JOIN
。
【讨论】:
以上是关于SQL Server:查找缺失 ID 的有效方法的主要内容,如果未能解决你的问题,请参考以下文章