【问题标题】:How do I find the first numerically sorted integer NOT in this list?如何找到不在此列表中的第一个按数字排序的整数?
【发布时间】:2018-04-11 17:46:24
【问题描述】:

我想浏览一个数字列表,并在列表按数字排序时找到第一个丢失的数字。

例如,考虑从查询中得到的一组数字:

A
---
0
1
3
4
5

我需要一个查询以按从低到高的数字顺序遍历此列表,并返回从数字列表中丢失的第一个数字,否则这些数字将是完整的并按数字排序。所以在上面的例子中,查询应该返回 2。下面是更多示例。要求:必须从值 0 开始检查,必须返回 NUMERICALLY SORTED 列表中缺少的第一个值,必须只返回 1 个数字。

A
---
1
3
4
5

/* return 0 */

A
---
0
1
2
3
4
5
8
9

/* return 6*/

【问题讨论】:

    标签: sql database sorting sql-server-2008-r2


    【解决方案1】:

    以下查询找到序列中第一个不存在的行,但未能找到零和序列开始之间的数字:

    SELECT 
      MIN(T1.a + 1) 
      FROM A as T1 LEFT OUTER JOIN  A as T2 ON T1.a + 1  = T2.a 
      WHERE T1.a > -1 AND T2.a IS NULL;
    

    基本思想 - 找到所有对行 + 前一行,并使用不完整的对来推导出缺少的值。

    插图:

    CREATE TABLE A (a int);
    INSERT INTO A VALUES (0) ;
    INSERT INTO A VALUES (1) ;
    INSERT INTO A VALUES (2) ;
    INSERT INTO A VALUES (3) ;
    --INSERT INTO A VALUES (4) ;
    INSERT INTO A VALUES (5) ;
    INSERT INTO A VALUES (6) ;
    INSERT INTO A VALUES (7) ;
    
    SELECT 
      T1.a, T2.a FROM A as T1 LEFT OUTER JOIN  A as T2 ON T1.a + 1  = T2.a;
    

    输出:

    0   1
    1   2
    2   3
    3   (null)
    5   6
    6   7
    7   (null)
    

    此解决方案is slower 在 MS SQL 上不存在。我发现这个事实很有趣,连接应该更容易优化。

    【讨论】:

    • 第一个语句是关于未能捕获丢失的0
    • 那么您不知道有一种解决方案可以解决缺失 0 的问题吗?
    • 那么您不知道有一种解决方案可以解决缺失 0 的问题吗?
    • 我很快就找不到了,没有。
    【解决方案2】:

    使用not exists:

    select top 1 a + 1
    from t
    where not exists (select 1 from t t2 where t2.a = t.a + 1)
    order by a;
    

    【讨论】:

    • 外连接不是更有效吗?
    • 这行得通,但我不太明白。 WHERE 子句在做什么?在我看来,它正在自己加入表格,并排除t2 的第一行和t 的最后一行加入。然后,它选择一个常数 1x 返回的行数。但是它想找到这个子查询在哪里没有返回任何行,这就是我感到困惑的地方。
    • 第二次测试失败(缺少 0)
    【解决方案3】:

    使用递归 CTE:

    declare @upperBound int
    select @upperBound = max(x) from yourTable    
    
    ;with a as (
    select 0 x
    union ALL
    SELECT x + 1
    from a
    where a < @upperBound
    )
    
    select min(a.x)
    from yourTable yt
     right join a
      on yt.x = a.x
    where yt.x is null
    

    【讨论】:

    • 我无法指定整数列表的上限。
    • @DarthVoid 你的意思是使用 99 以外的数字?
    • 对。如果我正确理解了您的答案,则您选择了任意高值进行检查。在我的情况下,我不能假设一个上限。
    • 将 99 更改为 (SELECT MAX(a) FROM yourtable)(或对其进行参数化)。不过,在较大的结果集上可能会很慢。
    【解决方案4】:

    我会使用数字表,也称为计数表。您可以根据需要即时创建一个,但这确实效率低下,而且用途很普遍,以至于我倾向于在每台服务器上都已经构建了一个,并完全编入索引。有很多关于它们的文章——这里有一个简单的例子Number tables explained part 1

    假设你已经建立了一个TALLY表,列也叫TALLY,你的表是DATA,要检查的列是ID

    找到我们需要检查的最大数字。 (@上界) 然后从统计表中选择每一个有效行,左外连接你的数据表,只保留不匹配的行。最左边的 Tally 是你想要的数字

    DECLARE @UpperBound int = (select MAX(ID) from DATA) 
    
    SELECT TOP 1 T.TALLY
    FROM TALLY T
    LEFT OUTER JOIN DATA D on D.ID = T.TALLY
    WHERE T.TALLY >= 0  --Lowest number to check
    AND T.TALLY < @UpperBound  --got to be smaller than this
    AND D.ID IS NULL  --only want rows that don't match
    ORDER BY T.TALLY ASC
    

    【讨论】:

      猜你喜欢
      • 2015-11-25
      • 1970-01-01
      • 2012-07-08
      • 2013-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      • 1970-01-01
      相关资源
      最近更新 更多