SQL Server:在计数器列中查找间隙的脚本或存储过程

SQL Server:在计数器列中查找间隙的脚本或存储过程,sql,sql-server,stored-procedures,Sql,Sql Server,Stored Procedures,我们有一个SQL Server表amPOrder,其列PONumber VARCHAR30以COM的形式保存采购订单号,即前缀COM后跟一个长度为8个字符的前导零填充整数。采购订单编号是连续的,但不一定是唯一的。可以有一个活动采购订单和草稿共享同一个采购订单编号 由于软件中最近发现的一个bug,PONumber被错误地创建,其影响如下: PONumber之间的许多间隙导致大约95%的可能值被浪费 我们正在接近允许的最大值COM9999最大可能值 我们希望确定自由的PONumber值,即10000

我们有一个SQL Server表amPOrder,其列PONumber VARCHAR30以COM的形式保存采购订单号,即前缀COM后跟一个长度为8个字符的前导零填充整数。采购订单编号是连续的,但不一定是唯一的。可以有一个活动采购订单和草稿共享同一个采购订单编号

由于软件中最近发现的一个bug,PONumber被错误地创建,其影响如下:

PONumber之间的许多间隙导致大约95%的可能值被浪费 我们正在接近允许的最大值COM9999最大可能值 我们希望确定自由的PONumber值,即100000到900000之间的int值,对于这些值,没有匹配的PONumber值,并将它们插入到一个新表中,从中我们可以使用间隙

创建表修复\u amPOrder\u PONumber 左值INT主键, 卢瑟斯皮德酒店 如果没有匹配的PONumber,可以在插入当前值的两个整数之间进行循环吗

解决了的 从@john joseph answer开始,我们做了以下工作:

创建一个fillGap存储过程,该存储过程接受起始值和要插入的行数: 创建过程填充间隙 @左值INT, @泰晤士报 像 开始 当@times>0开始时 插入固定顺序编号 左值 价值观 @左值 设置@lValue=@lValue+1 设置@times=@times-1 终止 终止 去 使用光标在已识别的间隙上迭代,并对每个间隙执行fillGap。 声明@lValue INT,@times INT 声明游标的元素游标 选择try_castreplace[PreviousPONumber],'COM',INT+1为自由值,Gap-1为时间 从…起 选择*,将“\u castreplace[PONumber]”、“COM”作为INT-将“\u castreplace[PreviousPONumber]”、“COM”作为INT作为Gap 从…起 选择[PONumber]、滞后[PONumber]、1超过订单[PONumber]作为上一个PONumber 来自[amPOrder]src1 src2 其中间隙>1 开放游标元素 从cursorElement获取下一个到@lValue,@次 而@@FETCH\u STATUS=0 开始 EXEC fillGap@lValue,@次 从cursorElement获取下一个到@lValue,@次 终止 闭合游标元素 解除分配游标元素 去 这种方法可能对某些人有用,特别是因为识别差距和处理这些差距是明显不同的

解决2 感谢@larnu提供了最快的解决方案:

-从fix_amPOrder_PONumber中删除 具有 N作为从值NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL中选择N作为NN, 通过从N N1、N N2、N N3、N N4、N N5、N N6中选择NULL-1作为I,在订单上计数为选择行_编号, 所有组件选择组件'COM',右组件'000000',I,6作为组件从Tally, 通信组件 从所有COM中选择COM 其中COM> 从amPOrder中选择TOP1 PONumber,其中PONumber不为空,按PONumber ASC排序 和COM< 从amPOrder中选择TOP1 PONumber,其中PONumber不为空,按PONumber DESC排序 插入固定顺序编号 左值 选择try_castreplaceC.COM,'COM',作为INT 从COMs AS C 在C.COM=PO.PONumber上以PO的形式左键加入AMPO订单 其中PO.PONumber为空 C.COM订购 去
谢谢大家的帮助。

查找缺失值的最快方法是使用计数生成所有值,然后左键联接:

以N作为 选择N 来自valuesnall,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULLNN, 算作 通过选择NULL-1作为I,在订单上选择行号 从N N1,N N2,N N3,N N4,N N5,N N6, 通信组件 选择CONCAT'COM',rightcat'000000',I,6作为COM 从理货 选择C.COM 来自COMs C 左键连接dbo.YourTable YT ON C.COM=YT.PONumber 其中YT.PONumber为空;
然后,您可以轻松地对此进行操作以插入值,或者执行某种更新。

查找缺失值的最快方法是使用计数生成所有值,然后左连接:

以N作为 选择N 来自valuesnall,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULLNN, 算作 通过选择NULL-1作为I,在订单上选择行号 从N N1,N N2,N N3,N N4,N N5,N N6, 通信组件 选择CONCAT'COM',rightcat'000000',I,6作为COM 从理货 选择C.COM 来自COMs C 左键连接dbo.YourTable YT ON C.COM=YT.PONumber 其中YT.PONumber为空;
然后,您可以轻松地对此进行操作以插入值,或者执行某种更新。

您可以使用SQL LAG函数在按订单号排序记录集后访问以前的订单号。结合一些算术来找出你的差距

    select *
from 
(
    select
         *
        ,try_cast(replace([PONumber],'COM','') as int) - try_cast(replace([PreviousPONumber],'COM','') as int) as Gap
    from
    (
        select 
             [PONumber]
            ,lag([PONumber],1) over (order by [PONumber]) as PreviousPONumber
        from [amPOrder]
    ) src1
) src2
where Gap > 1
这给你


在按采购订单编号对记录集进行排序后,可以使用SQL LAG函数访问以前的采购订单编号。结合一些算术来找出你的差距

    select *
from 
(
    select
         *
        ,try_cast(replace([PONumber],'COM','') as int) - try_cast(replace([PreviousPONumber],'COM','') as int) as Gap
    from
    (
        select 
             [PONumber]
            ,lag([PONumber],1) over (order by [PONumber]) as PreviousPONumber
        from [amPOrder]
    ) src1
) src2
where Gap > 1
这给你

感谢
谢谢你的回答。我无法正确地实现它,但它对我来说仍然很有见地。我不确定为什么您的查询有时执行得很好,但在Msg 207,级别16,状态1,第21行无效列名“PONumber”的情况下大部分时间都失败。即使我正确地更新了dbo.yourtable,查询有时也不会这样做,@popgosthewza。如果您遇到该错误,那是因为您要查询的表中不存在该列。我发现了我的错误。。。并且能够在一次查询中完成整个任务。非常感谢。谢谢你的回答。我无法正确地实现它,但它对我来说仍然很有见地。我不确定为什么您的查询有时执行得很好,但在Msg 207,级别16,状态1,第21行无效列名“PONumber”的情况下大部分时间都失败。即使我正确地更新了dbo.yourtable,查询有时也不会这样做,@popgosthewza。如果您遇到该错误,那是因为您要查询的表中不存在该列。我发现了我的错误。。。并且能够在一次查询中完成整个任务。非常感谢。谢谢你的回答。我已经用我们提出的详细解决方案更新了我的问题。谢谢你的回答。我已经用我们提出的详细解决方案更新了我的问题。这是一个非常慢的方法;它的表现将非常糟糕。当我给你一个理货选项时,为什么要用这个呢?与可能需要几分钟的事情相比,它需要几秒钟的时间?Larnu承认它很慢,而且可能很麻烦。事实上,我不是DBA,而是产品所有者,我需要一个快速的解决方案。内部DBA在4周内没有提出解决方案。因为这应该是一个“一次性”操作,而不是生活在生产环境中,我必须解决我能得到什么。你的理货选项非常有趣,尽管它超出了我的能力范围,我在几个小时后无法完成;它的表现将非常糟糕。当我给你一个理货选项时,为什么要用这个呢?与可能需要几分钟的事情相比,它需要几秒钟的时间?Larnu承认它很慢,而且可能很麻烦。事实上,我不是DBA,而是产品所有者,我需要一个快速的解决方案。内部DBA在4周内没有提出解决方案。因为这应该是一个“一次性”操作,而不是生活在生产环境中,我必须解决我能得到什么。你的理货选项非常有趣,尽管它超出了我的能力范围,而且我在几个小时后无法完成。