查找SQL中的所有整数间隙

时间:2013-09-09 13:17:34

标签: sql sql-server sql-server-2008

我有一个数据库,用于存储我从外部源引入的游戏的不同匹配信息。由于一些问题,数据库中偶尔会出现间隙(可能是1个缺少ID到几百个)。我希望让程序获取缺失游戏的数据,但我需要先获取该列表。

这是表的格式:

id (pk-identity)  |  GameID (int)  |  etc.  |  etc.  

我曾想过编写一个程序来运行循环并查询从1开始的每个GameID,但似乎应该有一种更有效的方法来获取丢失的数字。

使用SQL Server是否有一种简单有效的方法可以找到范围内所有缺失的数字?

6 个答案:

答案 0 :(得分:5)

想法是看差距的起点。我假设您使用的是SQL Server 2012,因此具有lag()lead()函数。以下内容获取下一个id

select t.*, lead(id) over (order by id) as nextid
from t;

如果存在差距,则nextid <> id+1。您现在可以使用where

来表征差距
select id+1 as FirstMissingId, nextid - 1 as LastMissingId
from (select t.*, lead(id) over (order by id) as nextid
      from t
     ) t
where nextid <> id+1;

编辑:

如果没有lead(),我会对相关的子查询做同样的事情:

select id+1 as FirstMissingId, nextid - 1 as LastMissingId
from (select t.*,
             (select top 1 id
              from t t2
              where t2.id > t.id
              order by t2.id
             ) as nextid
      from t
     ) t
where nextid <> id+1;

假设id是表中的主键(或者它只是一个索引),两种方法都应该具有合理的性能。

答案 1 :(得分:1)

Numbers table!

CREATE TABLE dbo.numbers (
   number int NOT NULL
)

ALTER TABLE dbo.numbers
ADD
   CONSTRAINT pk_numbers PRIMARY KEY CLUSTERED (number)
     WITH FILLFACTOR = 100
GO

INSERT INTO dbo.numbers (number)
SELECT (a.number * 256) + b.number As number
FROM     (
        SELECT number
        FROM   master..spt_values
        WHERE  type = 'P'
        AND    number <= 255
       ) As a
 CROSS
  JOIN (
        SELECT number
        FROM   master..spt_values
        WHERE  type = 'P'
        AND    number <= 255
       ) As b
GO

然后你可以在两张桌子之间执行OUTER JOIN或EXISTS`并找出差距......

SELECT *
FROM   dbo.numbers
WHERE  NOT EXISTS (
         SELECT *
         FROM   your_table
         WHERE  id = numbers.number
       )

-- OR

SELECT *
FROM   dbo.numbers
 LEFT
  JOIN your_table
    ON your_table.id = numbers.number
WHERE  your_table.id IS NULL

答案 2 :(得分:1)

我喜欢“差距和岛屿”的方法。它有点像这样:

WITH Islands AS (
    SELECT GameId, GameID - ROW_NUMBER() OVER (ORDER BY GameID) AS [IslandID]
    FROM dbo.yourTable
)
SELECT MIN(GameID), MAX(Game_id)
FROM Islands
GROUP BY IslandID

该查询将为您提供连续范围的列表。从那里,您可以自行加入该结果集(在连续的IslandID上)以获得差距。虽然有一些工作让IslandIDs本身是连续的。所以,扩展上面的查询:

WITH 
cte1 AS (
    SELECT GameId, GameId - ROW_NUMBER() OVER (ORDER BY GameId) AS [rn]
    FROM dbo.yourTable
)
, cte2 AS (
    SELECT [rn], MIN(GameId) AS [Start], MAX(GameId) AS [End]
    FROM cte1
    GROUP BY [rn]
)
,Islands AS (
    SELECT ROW_NUMBER() OVER (ORDER BY [rn]) AS IslandId, [Start], [End]
  from cte2
)

SELECT a.[End] + 1 AS [GapStart], b.[Start] - 1 AS [GapEnd]
FROM Islands AS a
LEFT JOIN Islands AS b
    ON a.IslandID + 1 = b.IslandID

答案 3 :(得分:1)

     SELECT * FROM #tab1
            id          col1
            ----------- --------------------
            1           a
            2           a
            3           a
            8           a
            9           a
            10          a
            11          a
            15          a
            16          a
            17          a
            18          a

 WITH cte (id,nextId) as
                (SELECT t.id, (SELECT TOP 1 t1.id FROM #tab1 t1 WHERE t1.id > t.id) AS nextId  FROM #tab1 t)

 SELECT id AS 'GapStart', nextId AS 'GapEnd' FROM cte
                WHERE id + 1 <> nextId

    GapStart    GapEnd
    ----------- -----------
    3           8
    11          15

答案 4 :(得分:0)

试试这个(这从1开始覆盖多达10000个Ids,如果你需要更多,你可以在下面的Numbers表中添加更多):

;WITH Digits AS (
    select Digit 
    from ( values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) as t(Digit))
,Numbers AS (
    select u.Digit 
          + t.Digit*10 
          + h.Digit*100 
          + th.Digit*1000
          + tth.Digit*10000 
          --Add 10000, 100000 multipliers if required here.
          as myId
    from Digits u
    cross join Digits t
    cross join Digits h
    cross join Digits th
    cross join Digits tth
    --Add the cross join for higher numbers 
    )
SELECT myId 
FROM Numbers
WHERE myId NOT IN (SELECT GameId FROM YourTable)

答案 5 :(得分:0)

问题:我们需要在id字段

中找到差距范围
SELECT * FROM #tab1

id          col1
----------- --------------------
1           a  
2           a  
3           a  
8           a    
9           a  
10          a  
11          a  
15          a  
16          a  
17          a  
18          a

解决方案

WITH cte (id,nextId) as
(SELECT t.id, (SELECT TOP 1 t1.id FROM #tab1 t1 WHERE t1.id > t.id) AS nextId  FROM #tab1 t)

SELECT id + 1, nextId - 1 FROM cte
WHERE id + 1 <> nextId

输出

GapStart    GapEnd
----------- -----------
4           7
12          14