通过单个sql查询查找缺少的数字

时间:2014-07-03 19:48:14

标签: sql sql-server

任务如下:我有一个表“数字”,只有一列“数字”。此表中的数字在范围a ... b中,每个特定数字仅出现在表格中一次。我需要找到范围a..b中缺少的数字。还有一件事是范围可能非常大,性能应该仍然很好。我想出了找到所有数字对的想法,在这些数字对之间缺少数字,所以我们不必比较这个范围内的所有数字并以这种方式提高性能。下一个查询返回所有这些数字对:

     select N1.number as num1, MIN(N2.number)as num2 from numbers N1 join numbers N2 on N2.number > N1.number 
                                            and N2.number - N1.number > 1
                                            and not exists (select * from numbers where number > N1.number and number < n2.number)
                                            group by n2.number, n1.number 

集1 2 3 4 6 8 10的结果是:

4   6
6   8
8   10

现在我想列出所有数字,但是我坚持了,我没有在其他select语句或其他任何内容中传递这些数字对并使其成为一个查询。有任何想法吗?它有可能吗?

4 个答案:

答案 0 :(得分:0)

您必须有一个所有号码列表,以便与(here's an example for that)进行比较。现在,如果你在名为“all_numbers”的表中有这些数字,你可以这样做:

select a.number from all_numbers a
where a.number not in (select N1.number from numbers N1)

或者,您可以通过添加以下过滤条件来限制“缺失”数字的范围:

and a.number between (select min(number) from numbers) 
             and (select max(number) from numbers)

这取决于你需要多么复杂,但你肯定需要从所有相关数字的列表开始。

答案 1 :(得分:0)

我认为这是您需要的查询:

select nstart, 
       nend
  from (select m.startNumber + 1 as nstart,
              (select min(startNumber) - 1 
                 from numbers x 
                 where x.startNumber > m.startNumber) as nend
          from numbers m 
                left join
                   (select startNumber-1 startNumber 
                      from numbers r) r 
                on (m.startNumber = r.startNumber)
         where r.startNumber is null
       ) x
 where nend is not null
 order by nstart

在这里查看小提琴:http://sqlfiddle.com/#!2/720b1/4

需要注意的重要事项是@GordonLinoff发表的评论:

Without some constraints on the problem, it is hard to come up with an 
efficient solution. After all, the table could contain two values, 
such as 1 and 1,000,000,000

答案 2 :(得分:0)

谢谢大家,伙计们,我将你提出的所有解决方案结合起来,最后提出了这个解决方案:

    WITH
    L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
    L1   AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
    L2   AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
    L3   AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
    L4   AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
    L5   AS(SELECT 1 AS c FROM L4 AS A CROSS JOIN L3 AS B),
    Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)

    select n from Nums as Nmbrs

    inner join

    (select N1.number as num1, MIN(N2.number)as num2 from numbers N1 join numbers N2 on N2.number > N1.number 
                                            and N2.number - N1.number > 1
                                            and not exists (select * from numbers where number > N1.number and number < n2.number)
                                            group by n2.number, n1.number) T on Nmbrs.n>num1 and Nmbrs.n < num2

    order by Nmbrs.n    

工作速度非常快,我使用Fibonacci数字进行测试,1 ... 1 000 000在7秒内处理,1 ... 1 000 000 000在69秒内处理,以及最重要的是什么,它&& #39;单个查询。

答案 3 :(得分:0)

我认为使用CTE的递归查询可以对您有所帮助:

WITH   CTE (i, sw)
AS     (SELECT 1 AS i, 'SW_00000' -- anchor member
    UNION ALL
    SELECT i + 1, 'SW_' + RIGHT('00000' + cast(i+1 as varchar),5)  -- recursive member
    FROM   cte
    WHERE  i < 5000 -- terminator
   )
SELECT i,sw  -- Display CTE result
FROM   cte
where sw not in (  -- exclude existing values
select... <query existing stuff here> 
)

OPTION (MAXRECURSION 5000); -- default max for recursion = 200