【问题标题】:SQL query to find Missing sequence numbers查找缺失序列号的 SQL 查询
【发布时间】:2010-11-06 15:24:52
【问题描述】:

我有一个名为sequence 的列。此列中的数据看起来像 1、2、3、4、5、7、9、10、15。

我需要从表中找到缺失的序列号。什么 SQL 查询会从我的表中找到缺失的序列号?我期待这样的结果

Missing numbers
---------------
6  
8  
11  
12  
13  
14  

我只使用一张桌子。我尝试了下面的查询,但没有得到我想要的结果。

select de.sequence + 1 as sequence from dataentry as de 
left outer join dataentry as de1 on de.sequence + 1 = de1.sequence
where de1.sequence is null  order by sequence asc;

【问题讨论】:

标签: sql-server-2005 gaps-and-islands


【解决方案1】:

怎么样:

  select (select isnull(max(val)+1,1) from mydata where val < md.val) as [from],
     md.val - 1 as [to]
  from mydata md
  where md.val != 1 and not exists (
        select 1 from mydata md2 where md2.val = md.val - 1)

给出总结结果:

from        to
----------- -----------
6           6
8           8
11          14

【讨论】:

  • 好像比我的简单,不要用临时表……我会保存这段sn-p的代码试试看。
  • 这非常低效......请参阅基于左连接的答案
【解决方案2】:

我知道这是一篇很老的帖子,但我想添加这个我找到的解决方案 HERE 以便我可以更轻松地找到它:

WITH Missing (missnum, maxid)
AS
(
 SELECT 1 AS missnum, (select max(id) from @TT)
 UNION ALL
 SELECT missnum + 1, maxid FROM Missing
 WHERE missnum < maxid
)
SELECT missnum
FROM Missing
LEFT OUTER JOIN @TT tt on tt.id = Missing.missnum
WHERE tt.id is NULL
OPTION (MAXRECURSION 0); 

【讨论】:

    【解决方案3】:

    试试这个:

    declare @min int
    declare @max int
    
    select @min = min(seq_field), @max = max(seq_field) from [Table]
    
    create table #tmp (Field_No int)
    while @min <= @max
    begin
       if not exists (select * from [Table] where seq_field = @min)
          insert into #tmp (Field_No) values (@min)
       set @min = @min + 1
    end
    select * from #tmp
    drop table #tmp
    

    【讨论】:

      【解决方案4】:

      最好的解决方案是使用带有序列的临时表。假设您构建了这样一个表,带有 NULL 检查的 LEFT JOIN 应该可以完成这项工作:

      SELECT      #sequence.value
      FROM        #sequence
      LEFT JOIN   MyTable ON #sequence.value = MyTable.value
      WHERE       MyTable.value IS NULL
      

      但是,如果您必须经常重复此操作(并且对于数据库中的 1 个序列更多),我将创建一个“静态数据”表并使用脚本将其填充到所有您需要的表格。

      【讨论】:

      【解决方案5】:
      SELECT CASE WHEN MAX(column_name) = COUNT(*)
      THEN CAST(NULL AS INTEGER)
      -- THEN MAX(column_name) + 1 as other option
      WHEN MIN(column_name) > 1
      THEN 1
      WHEN MAX(column_name) <> COUNT(*)
      THEN (SELECT MIN(column_name)+1
      FROM table_name
      WHERE (column_name+ 1)
      NOT IN (SELECT column_name FROM table_name))
      ELSE NULL END
      FROM table_name;
      

      【讨论】:

      • 在 MySql 中使用注释掉的行而不是强制转换此选项对我有用。谢谢。
      【解决方案6】:

      这是一个用于创建存储过程的脚本,该存储过程返回给定日期范围内缺少的序列号。

      CREATE PROCEDURE dbo.ddc_RolledBackOrders 
      -- Add the parameters for the stored procedure here
      @StartDate DATETIME ,
      @EndDate DATETIME
      AS 
          BEGIN
      
          SET NOCOUNT ON;
      
          DECLARE @Min BIGINT
          DECLARE @Max BIGINT
          DECLARE @i BIGINT
      
          IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL 
              BEGIN
                  DROP TABLE #TempTable
              END
      
          CREATE TABLE #TempTable
              (
                TempOrderNumber BIGINT
              )
      
          SELECT  @Min = ( SELECT MIN(ordernumber)
                           FROM   dbo.Orders WITH ( NOLOCK )
                           WHERE OrderDate BETWEEN @StartDate AND @EndDate)
          SELECT  @Max = ( SELECT MAX(ordernumber)
                           FROM   dbo.Orders WITH ( NOLOCK )
                           WHERE OrderDate BETWEEN @StartDate AND @EndDate)
          SELECT  @i = @Min
      
          WHILE @i <= @Max 
              BEGIN
                  INSERT  INTO #TempTable
                          SELECT  @i
      
                  SELECT  @i = @i + 1
      
              END
      
          SELECT  TempOrderNumber
          FROM    #TempTable
                  LEFT JOIN dbo.orders o WITH ( NOLOCK ) ON tempordernumber = o.OrderNumber
          WHERE   o.OrderNumber IS NULL
      
      END
      

      【讨论】:

        【解决方案7】:

        是不是所有给定的解决方案都太复杂了? 这不是更简单吗:

        SELECT  *
        FROM    (SELECT  row_number() over(order by number) as N from master..spt_values) t
        where   N not in (select 1 as sequence union  
                select 2 union 
                select 3 union 
                select 4 union 
                select 5 union 
                select 7 union 
                select 10 union 
                select 15
                )
        

        【讨论】:

        • 我有 50000 条记录..那么我该如何选择
        • 这看起来很有趣,让我担心的是性能和 spt_values 的使用......
        • 如果他的表的行数(或更高的序列号)比“spt_values”表中的行数多怎么办?
        • 您可以轻松地对表进行交叉连接以获得更多行。通常你的数据库中有一个数字表,它被索引并且速度飞快。
        【解决方案8】:

        这是我对这个问题的解释,将内容放在我可以在脚本的其余部分轻松访问的 Table 变量中。

        DECLARE @IDS TABLE (row int, ID int)
        
        INSERT INTO @IDS
        select      ROW_NUMBER() OVER (ORDER BY x.[Referred_ID]), x.[Referred_ID] FROM
        (SELECT      b.[Referred_ID] + 1 [Referred_ID]
        FROM        [catalog].[dbo].[Referrals] b) as x
        LEFT JOIN   [catalog].[dbo].[Referrals] a ON x.[Referred_ID] = a.[Referred_ID]
        WHERE       a.[Referred_ID] IS NULL
        
        select * from @IDS
        

        【讨论】:

          【解决方案9】:

          只是为了好玩,我决定发布我的解决方案。
          我的表中有一个身份列,我想找到丢失的发票号码。 我查看了所有我能找到的示例,但它们不够优雅。

          CREATE VIEW EENSkippedInvoicveNo
          AS
          
          SELECT CASE WHEN MSCNT = 1 THEN CAST(MSFIRST AS VARCHAR (8)) ELSE
              CAST(MSFIRST AS VARCHAR (8)) + ' - ' + CAST(MSlAST AS VARCHAR (8))  END AS MISSING,
          MSCNT, INV_DT  FROM ( 
          select  invNo+1  as Msfirst, inv_no -1 as Mslast, inv_no - invno -1 as msCnt, dbo.fmtdt(Inv_dt)  AS INV_dT
          from (select inv_no as invNo,  a4glidentity + 1  as a4glid 
          from oehdrhst_sql where inv_dt > 20140401) as s
          inner Join oehdrhst_sql as h
          on a4glid = a4glidentity 
          where inv_no - invno <> 1
          ) AS SS
          

          【讨论】:

            【解决方案10】:
            DECLARE @MaxID INT = (SELECT MAX(timerecordid) FROM dbo.TimeRecord)
            
            SELECT SeqID AS MissingSeqID
            FROM (SELECT ROW_NUMBER() OVER (ORDER BY column_id) SeqID from sys.columns) LkUp
            LEFT JOIN dbo.TimeRecord t ON t.timeRecordId = LkUp.SeqID
            WHERE t.timeRecordId is null and SeqID < @MaxID
            

            我在这里找到了这个答案: http://sql-developers.blogspot.com/2012/10/how-to-find-missing-identitysequence.html

            我一直在寻找解决方案并找到了很多答案。这是我用过的,效果很好。我希望这可以帮助任何寻找类似答案的人。

            【讨论】:

              【解决方案11】:
               -- This will return better Results
                  -- ----------------------------------
                  ;With CTERange
                  As (
                  select (select isnull(max(ArchiveID)+1,1) from tblArchives where ArchiveID < md.ArchiveID) as [from],
                       md.ArchiveID - 1 as [to]
                    from tblArchives md
                    where md.ArchiveID != 1 and not exists (
                          select 1 from tblArchives md2 where md2.ArchiveID = md.ArchiveID - 1)
                  ) SELECT [from], [to], ([to]-[from])+1 [total missing]
                  From CTERange 
                  ORDER BY ([to]-[from])+1 DESC;
              
              
              from     to     total missing
              ------- ------- --------------
              6        6      1 
              8        8      1
              11       14     4
              

              【讨论】:

                【解决方案12】:
                DECLARE @TempSujith TABLE
                (MissingId int)
                
                Declare @Id Int
                DECLARE @mycur CURSOR
                SET @mycur = CURSOR FOR Select  Id From tbl_Table
                
                OPEN @mycur
                
                FETCH NEXT FROM @mycur INTO @Id
                Declare @index int
                Set @index = 1
                WHILE @@FETCH_STATUS = 0
                BEGIN
                    if (@index < @Id)
                    begin
                        while @index < @Id
                        begin
                            insert into @TempSujith values (@index)
                            set @index = @index + 1
                        end
                    end
                    set @index = @index + 1
                FETCH NEXT FROM @mycur INTO @Id
                END
                Select Id from tbl_Table
                select MissingId from @TempSujith
                

                【讨论】:

                  【解决方案13】:

                  创建一个有用的Tally table

                  -- can go up to 4 million or 2^22
                  select top 100000 identity(int, 1, 1) Id
                  into Tally
                  from master..spt_values
                  cross join master..spt_values
                  

                  将其编入索引,或将该单列设为 PK。 然后使用 EXCEPT 来获取您丢失的号码。

                  select Id from Tally where Id <= (select max(Id) from TestTable)
                  except
                  select Id from TestTable
                  

                  【讨论】:

                    【解决方案14】:

                    您也可以使用 CTE 之类的东西来生成完整序列:

                    创建表#tmp(sequence int) 插入 #tmp(sequence) 值 (1) 插入 #tmp(sequence) 值 (2) 插入 #tmp(sequence) 值 (3) 插入 #tmp(sequence) 值 (5) 插入 #tmp(sequence) 值 (6) 插入 #tmp(sequence) 值 (8) 插入 #tmp(sequence) 值 (10) 插入 #tmp(sequence) 值 (11) 插入 #tmp(sequence) 值 (14)
                        DECLARE @max INT
                        SELECT @max = max(sequence) from #tmp;
                    
                        with full_sequence
                        (
                            Sequence
                        )
                        as
                        (
                            SELECT 1 Sequence
                    
                            UNION ALL
                    
                            SELECT Sequence + 1
                            FROM full_sequence
                            WHERE Sequence < @max
                        )
                    
                        SELECT
                            full_sequence.sequence
                        FROM
                            full_sequence
                        LEFT JOIN
                            #tmp
                        ON
                            full_sequence.sequence = #tmp.sequence
                        WHERE
                            #tmp.sequence IS NULL
                    

                    嗯 - 由于某种原因,格式在这里不起作用?谁能看出问题?

                    【讨论】:

                    • 很好,但是:“SELECT 1”应该替换为“SELECT MIN(...)”。如果你有超过 100 个项目,递归将不起作用。
                    【解决方案15】:

                    我制作了一个 proc,因此您可以发送表名和密钥,结果是给定表中缺失数字的列表

                    SET ANSI_NULLS ON
                    GO
                    
                    SET QUOTED_IDENTIFIER ON
                    GO
                    
                    create PROCEDURE [dbo].[action_FindMissing_Autoincremnt]
                    (
                    @tblname as nvarchar(50),
                    @tblKey as nvarchar(50)
                    )
                    AS
                    BEGIN
                        SET NOCOUNT ON;
                    
                    declare @qry nvarchar(4000)
                    
                    
                    
                    set @qry = 'declare @min int ' 
                    set @qry = @qry + 'declare @max int '
                    
                    set @qry = @qry +'select @min = min(' + @tblKey + ')'
                    set @qry = @qry + ', @max = max('+ @tblKey +') '
                    set @qry = @qry + ' from '+ @tblname 
                    
                    set @qry = @qry + ' create table #tmp (Field_No int)
                    while @min <= @max
                    begin
                       if not exists (select * from '+ @tblname +' where '+ @tblKey +' = @min)
                          insert into #tmp (Field_No) values (@min)
                       set @min = @min + 1
                    end
                    select * from #tmp order by Field_No
                    drop table #tmp '
                    
                    exec sp_executesql @qry 
                    
                    END
                    GO
                    

                    【讨论】:

                      【解决方案16】:
                      SELECT TOP 1 (Id + 1)
                      FROM CustomerNumberGenerator
                      WHERE (Id + 1) NOT IN ( SELECT Id FROM CustomerNumberGenerator )
                      

                      为我的公司开发客户编号生成器。不是最有效的,但绝对是最易读的

                      该表有一个 Id 列。 该表允许通过用户关闭序列手动插入 Id。 该方案解决了用户决定选择高数的情况

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2020-05-26
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多