我有一个数据库,用于存储我从外部源引入的游戏的不同匹配信息。由于一些问题,数据库中偶尔会出现间隙(可能是1个缺少ID到几百个)。我希望让程序获取缺失游戏的数据,但我需要先获取该列表。
这是表的格式:
id (pk-identity) | GameID (int) | etc. | etc.
我曾想过编写一个程序来运行循环并查询从1开始的每个GameID,但似乎应该有一种更有效的方法来获取丢失的数字。
使用SQL Server是否有一种简单有效的方法可以找到范围内所有缺失的数字?
答案 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)
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