如何在运行SQL的计数器中找到"差距"?

Tou*_*uko 97 sql gaps-and-islands

我想在SQL表的计数器列中找到第一个"间隙".例如,如果有值1,2,4和5,我想找出3.

我当然可以按顺序获取值并手动完成它,但我想知道是否有办法在SQL中执行它.

此外,它应该是非常标准的SQL,使用不同的DBMS.

Qua*_*noi 168

MySQLPostgreSQL:

SELECT  id + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )
ORDER BY
        id
LIMIT 1
Run Code Online (Sandbox Code Playgroud)

SQL Server:

SELECT  TOP 1
        id + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )
ORDER BY
        id
Run Code Online (Sandbox Code Playgroud)

Oracle:

SELECT  *
FROM    (
        SELECT  id + 1 AS gap
        FROM    mytable mo
        WHERE   NOT EXISTS
                (
                SELECT  NULL
                FROM    mytable mi 
                WHERE   mi.id = mo.id + 1
                )
        ORDER BY
                id
        )
WHERE   rownum = 1
Run Code Online (Sandbox Code Playgroud)

ANSI (无处不在,效率最低):

SELECT  MIN(id) + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )
Run Code Online (Sandbox Code Playgroud)

支持滑动窗口功能的系统:

SELECT  -- TOP 1
        -- Uncomment above for SQL Server 2012+
        previd
FROM    (
        SELECT  id,
                LAG(id) OVER (ORDER BY id) previd
        FROM    mytable
        ) q
WHERE   previd <> id - 1
ORDER BY
        id
-- LIMIT 1
-- Uncomment above for PostgreSQL
Run Code Online (Sandbox Code Playgroud)

  • @vulkanino:请他们保留缩进.另请注意,创意公共许可证要求您纹身我的昵称和问题"URL",尽管它可能是QR编码我认为. (37认同)
  • 这不会找到正确的初始差距.如果你有3,4,5,6,8.此代码将报告7,因为它甚至没有检查1.因此,如果您缺少起始号码,则必须检查该号码. (5认同)
  • 这很好,但如果我有`[1,2,11,12]`,那么这只会找到`3`.我喜欢它找到的是3-10而不是 - 基本上是每个差距的开始和结束.我知道我可能必须编写自己的利用SQL的python脚本(在我的案例中是MySql),但是如果SQL可以让我更接近我想要的东西(我有一个有200万行的表有间隙,所以我需要将它切成小块并在其上运行一些SQL).我想我可以运行一个查询来查找间隙的开始,然后另一个查找间隙的结束,并将它们"合并排序"两个序列. (4认同)
  • @Malkocoglu:如果表是空的,你将得到`NULL`,而不是'0`.所有数据库都是如此. (2认同)

小智 12

如果您的第一个值为id = 1,那么您的答案都可以正常工作,否则将无法检测到此差距.例如,如果您的表ID值为3,4,5,则查询将返回6.

我做了这样的事

SELECT MIN(ID+1) FROM (
    SELECT 0 AS ID UNION ALL 
    SELECT  
        MIN(ID + 1)
    FROM    
        TableX) AS T1
WHERE
    ID+1 NOT IN (SELECT ID FROM TableX) 
Run Code Online (Sandbox Code Playgroud)


Mic*_*ker 8

第一件事就是我的想法.不确定这样做是否一个好主意,但应该有效.假设表是t,并且列是c:

SELECT t1.c+1 AS gap FROM t as t1 LEFT OUTER JOIN t as t2 ON (t1.c+1=t2.c) WHERE t2.c IS NULL ORDER BY gap ASC LIMIT 1

编辑:这个可能更快(更短!):

SELECT min(t1.c)+1 AS gap FROM t as t1 LEFT OUTER JOIN t as t2 ON (t1.c+1=t2.c) WHERE t2.c IS NULL

  • 不,不,Eamon,`LEFT OUTER JOING t2` 需要你有 `t2` 表,它只是一个别名。 (2认同)

cha*_*aos 8

确实没有非常标准的SQL方法可以执行此操作,但是通过某种形式的限制子句,您可以执行此操作

SELECT `table`.`num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
LIMIT 1
Run Code Online (Sandbox Code Playgroud)

(MySQL,PostgreSQL)

要么

SELECT TOP 1 `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
Run Code Online (Sandbox Code Playgroud)

(SQL Server)

要么

SELECT `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
AND ROWNUM = 1
Run Code Online (Sandbox Code Playgroud)

(甲骨文)


May*_*ayo 6

这适用于SQL Server - 无法在其他系统中测试它,但它似乎是标准的......

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1))
Run Code Online (Sandbox Code Playgroud)

你也可以在where子句中添加一个起点......

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1)) AND ID > 2000
Run Code Online (Sandbox Code Playgroud)

因此,如果您有2000年,2001年,2002年和2005年,2003年和2004年不存在,它将返回2003年.