如何找到不存在的数字?

时间:2017-06-06 13:08:13

标签: sql oracle

我们在训练记录中有一个自定义数字字段,该数字按顺序记录,但存在间隙。我如何找到这些差距?考虑这个伪代码

SELECT MIN(X)
FROM DUAL
WHERE X BETWEEN 1 AND 999999
  AND X NOT IN (SELECT AG_TRNID
                FROM PS_TRAINING
                WHERE AG_TRNID = X)

这不起作用,“X”未知。

谢谢! 布鲁斯

7 个答案:

答案 0 :(得分:2)

这个答案假定您使用的是Oracle。

解决此问题的方法是创建一个包含范围内所有数字的结果集,然后加入到该结果集中。这样做的方法是使用递归查询:

SELECT     LEVEL AS x
FROM       DUAL
CONNECT BY LEVEL <= 999999

CONNECT BY是特定于Oracle的语法,只要谓词为真,就会告诉查询以递归方式运行。 level是一个伪列,仅存在于使用CONNECT BY表示递归级别的查询中。最终结果是,此查询将针对双重999,999次运行查询,每次都是递归中更深的级别。

鉴于这种生成数字的方法,将其插入您之前尝试过的查询中非常简单:

SELECT MIN (x)
FROM   (SELECT     LEVEL AS x
        FROM       DUAL
        CONNECT BY LEVEL <= 999999)
WHERE  x NOT IN (SELECT ag_trnid
                 FROM   ps_training
                 WHERE  ag_trnid = x)

答案 1 :(得分:1)

两个简单的例子。第一个是你的经典Gaps-and-Islands,第二个是使用ad-hoc计数表通过LEFT JOIN识别缺失元素。

以下内容是在SQL Server中创建的,但如果您的数据库支持窗口函数,那么它应该是一项小任务。

差距和群岛

Declare @YourTable table (X int)
Insert Into @YourTable values
(1),(2),(3),(5),(6),(10)

Select X1 = min(X)
      ,X2 = max(X)
 From (
        Select *
              ,Grp = X - Row_Number() over (Order by X)
         From @YourTable
     ) A
 Group By Grp

<强>返回

X1  X2
1   3
5   6
10  10

Ad-hoc Tally Table

Declare @YourTable table (X int)
Insert Into @YourTable values
(1),(2),(3),(5),(6),(10)


 ;with cte0(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
       cteN(N) As (Select Row_Number() over (Order By (Select NULL)) From cte0 N1, cte0 N2, cte0 N3, cte0 N4, cte0 N5, cte0 N6) -- 1 Million
Select N
 From  cteN
 Left  Join @YourTable on X=N
 Where X is Null
  and  N<=(Select max(X) from @YourTable)
 Order By N

<强>返回

N
4
7
8
9

答案 2 :(得分:1)

创建从min(ag_trnid)max(ag_trnid)的所有号码。从这些删除现有的数字:

with nums(num, maxnum) as
(
  select min(ag_trnid) as num, max(ag_trnid) as maxnum from ps_training
  union all
  select num + 1, maxnum from nums where num < maxnum
)
select num from nums
minus
select ag_trnid from ps_training;

如果您在最小1间隔之前考虑数字,请从0(或min(ag_trnid))开始,而不是ag_trnid

答案 3 :(得分:0)

我认为除了创建表的id以找到差距之外别无他法

declare @idMin bigint
declare @idMax bigint

set @idMin = (select min(AG_TRNID) from PS_TRAINING)
set @idMax = (select max(AG_TRNID) from PS_TRAINING)

create table numbers
(id bigint)

while (@idMax>@idMin)
begin
insert into numbers values(@idMin)

set @idMin=@idMin+1
end

select id from numbers 
where id not in (SELECT AG_TRNID FROM PS_TRAINING) 

答案 4 :(得分:0)

您可以创建一个&#34;虚拟&#34;包含1到999999之间所有数字的表,然后删除表中存在的数字:

select level
from dual
connect by level <= 999999
minus 
select ag_trnid
from ps_training;

以上将输出所有差距,而不仅仅是第一个。

connect by level <= 999999是一个未记录的技巧,用于生成从1到999999的所有数字。

答案 5 :(得分:0)

这是找到差距开始和结束数字的一种方法:

WITH sample_data AS (SELECT 1 ID FROM dual UNION ALL
                     SELECT 2 ID FROM dual UNION ALL
                     SELECT 4 ID FROM dual UNION ALL
                     SELECT 5 ID FROM dual UNION ALL
                     SELECT 8 ID FROM dual UNION ALL
                     SELECT 10 ID FROM dual UNION ALL
                     SELECT 20 ID FROM dual UNION ALL
                     SELECT 22 ID FROM dual UNION ALL
                     SELECT 23 ID FROM dual UNION ALL
                     SELECT 27 ID FROM dual UNION ALL
                     SELECT 28 ID FROM dual)
-- end of mimicking data in a table called "sample_data"
-- see SQL below:
SELECT prev_id + 1 first_number_in_gap,
       ID - 1 last_number_in_gap
FROM   (SELECT ID,
               LAG(ID, 1, ID - 1) OVER (ORDER BY ID) prev_id
        FROM   sample_data)
WHERE  ID - prev_id > 1;

FIRST_NUMBER_IN_GAP LAST_NUMBER_IN_GAP
------------------- ------------------
                  3                  3
                  6                  7
                  9                  9
                 11                 19
                 21                 21
                 24                 26

答案 6 :(得分:0)

最后,我创建了一个包含所有可能值的表,并从中选择了基表中不存在的最小值。它运作良好。我曾希望有一个sql语句来避免创建另一个对象,但是不能。

是的,这是甲骨文,我忽略了。

感谢大家的建议和帮助,非常感谢!