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 的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

查找缺失的对 (Oracle SQL)

如何在表 Sql 中查找缺失的数据

T-SQL - 使用按位运算查找缺失值[关闭]

SQL Server - 当 WHERE 原因是唯一 ID 时进行多次更新的最有效方法

在 SQL 中查找表之间缺失的数字

在表中查找缺失的序列