如何在运行计数器中找到SQL的“间隔”?

时间:2021-12-01 20:56:12

I'd like to find the first "gap" in a counter column in an SQL table. For example, if there are values 1,2,4 and 5 I'd like to find out 3.

我希望在SQL表中找到计数器列中的第一个“空白”。例如,如果有1、2、4和5的值,我想找出3。

I can of course get the values in order and go through it manually, but I'd like to know if there would be a way to do it in SQL.

我当然可以按顺序获取值,然后手动检查,但是我想知道是否有一种方法可以用SQL来完成。

In addition, it should be quite standard SQL, working with different DBMSes.

此外,它应该是标准的SQL,使用不同的dbms。

13 个解决方案

#1


137  

In MySQL and PostgreSQL:

在MySQL和PostgreSQL:

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

In SQL Server:

在SQL服务器:

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

In Oracle:

在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

ANSI (works everywhere, least efficient):

ANSI(适用于任何地方,效率最低):

SELECT  MIN(id) + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )

Systems supporting sliding window functions:

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

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

#2


9  

Your answers all work fine if you have a first value id = 1, otherwise this gap will not be detected. For instance if your table id values are 3,4,5, your queries will return 6.

如果您的第一个值id为1,那么您的答案都可以正常工作,否则将不会检测到这个间隙。例如,如果表id值为3,4,5,那么查询将返回6。

I did something like this

我做过类似的事情。

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) 

#3


7  

The first thing that came into my head. Not sure if it's a good idea to go this way at all, but should work. Suppose the table is t and the column is c:

我脑子里想到的第一件事。不确定这样做是否是个好主意,但应该行得通。假设表为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

选择t1。c+1与t的差值为t1左外连接t为t2 (t1.c+1=t2.c) t2。c为空阶,由间隙ASC极限1。

Edit: This one may be a tick faster (and shorter!):

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

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

选择min(t1.c)+1作为间隙,从t到t1左外连接t为t2 (t1.c+1=t2.c)。c是零

#4


6  

This works in SQL Server - can't test it in other systems but it seems standard...

这在SQL Server中工作——不能在其他系统中测试它,但是它看起来很标准……

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1))

You could also add a starting point to the where clause...

您还可以在where子句中添加一个起点……

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1)) AND ID > 2000

So if you had 2000, 2001, 2002, and 2005 where 2003 and 2004 didn't exist, it would return 2003.

如果你有2000年,2001年,2002年,2005年,2003年和2004年都不存在,它会返回2003年。

#5


4  

There isn't really an extremely standard SQL way to do this, but with some form of limiting clause you can do

实际上并没有一种非常标准的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

(MySQL, PostgreSQL)

(MySQL、PostgreSQL)

or

SELECT TOP 1 `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL

(SQL Server)

(SQL Server)

or

SELECT `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
AND ROWNUM = 1

(Oracle)

(Oracle)

#6


2  

Inner join to a view or sequence that has a all possible values.

具有所有可能值的视图或序列的内部连接。

No table? Make a table. I always keep a dummy table around just for this.

没有桌子吗?做一个表。我总是在这里放一个假桌子。

create table artificial_range( 
  id int not null primary key auto_increment, 
  name varchar( 20 ) null ) ;

-- or whatever your database requires for an auto increment column

insert into artificial_range( name ) values ( null )
-- create one row.

insert into artificial_range( name ) select name from artificial_range;
-- you now have two rows

insert into artificial_range( name ) select name from artificial_range;
-- you now have four rows

insert into artificial_range( name ) select name from artificial_range;
-- you now have eight rows

--etc.

insert into artificial_range( name ) select name from artificial_range;
-- you now have 1024 rows, with ids 1-1024

Then,

然后,

 select a.id from artificial_range a
 where not exists ( select * from your_table b
 where b.counter = a.id) ;

#7


1  

My guess:

我的猜测:

SELECT MIN(p1.field) + 1 as gap
FROM table1 AS p1  
INNER JOIN table1 as p3 ON (p1.field = p3.field + 2)
LEFT OUTER JOIN table1 AS p2 ON (p1.field = p2.field + 1)
WHERE p2.field is null;

#8


1  

For PostgreSQL

为PostgreSQL

An example that makes use of recursive query.

一个使用递归查询的例子。

This might be useful if you want to find a gap in a specific range (it will work even if the table is empty, whereas the other examples will not)

如果您希望在特定范围内找到空白,这可能是有用的(即使表是空的,它也能工作,而其他示例则不行)

WITH    
    RECURSIVE a(id) AS (VALUES (1) UNION ALL SELECT id + 1 FROM a WHERE id < 100), -- range 1..100  
    b AS (SELECT id FROM my_table) -- your table ID list    
SELECT a.id -- find numbers from the range that do not exist in main table
FROM a
LEFT JOIN b ON b.id = a.id
WHERE b.id IS NULL
-- LIMIT 1 -- uncomment if only the first value is needed

#9


0  

This one accounts for everything mentioned so far. It includes 0 as a starting point, which it will default to if no values exist as well. I also added the appropriate locations for the other parts of a multi-value key. This has only been tested on SQL Server.

这一点解释了到目前为止所提到的一切。它包含0作为起始点,如果不存在任何值,它将默认为。我还为多值键的其他部分添加了适当的位置。这只在SQL Server上进行了测试。

select
    MIN(ID)
from (
    select
        0 ID
    union all
    select
        [YourIdColumn]+1
    from
        [YourTable]
    where
        --Filter the rest of your key--
    ) foo
left join
    [YourTable]
    on [YourIdColumn]=ID
    and --Filter the rest of your key--
where
    [YourIdColumn] is null

#10


0  

select min([ColumnName]) from [TableName]
where [ColumnName]-1 not in (select [ColumnName] from [TableName])
and [ColumnName] <> (select min([ColumnName]) from [TableName])

#11


0  

Here is standard a SQL solution that runs on all database servers with no change:

下面是一个标准的SQL解决方案,它可以在所有数据库服务器上运行,并且不需要更改:

select min(counter + 1) FIRST_GAP
    from my_table a
    where not exists (select 'x' from my_table b where b.counter = a.counter + 1)
        and a.counter <> (select max(c.counter) from my_table c);

See in action for;

看到在行动;

#12


0  

It works for empty tables or with negatives values as well. Just tested in SQL Server 2012

它适用于空表或带负值的表。刚刚在SQL Server 2012中测试过

 select min(n) from (
select  case when lead(i,1,0) over(order by i)>i+1 then i+1 else null end n from MyTable) w

#13


0  

If You use Firebird 3 this is most elegant and simple:

如果您使用Firebird 3,这是最优雅和简单的:

select RowID
  from (
    select `ID_Column`, Row_Number() over(order by `ID_Column`) as RowID
      from `Your_Table`
        order by `ID_Column`)
    where `ID_Column` <> RowID
    rows 1

#1


137  

In MySQL and PostgreSQL:

在MySQL和PostgreSQL:

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

In SQL Server:

在SQL服务器:

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

In Oracle:

在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

ANSI (works everywhere, least efficient):

ANSI(适用于任何地方,效率最低):

SELECT  MIN(id) + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )

Systems supporting sliding window functions:

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

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

#2


9  

Your answers all work fine if you have a first value id = 1, otherwise this gap will not be detected. For instance if your table id values are 3,4,5, your queries will return 6.

如果您的第一个值id为1,那么您的答案都可以正常工作,否则将不会检测到这个间隙。例如,如果表id值为3,4,5,那么查询将返回6。

I did something like this

我做过类似的事情。

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) 

#3


7  

The first thing that came into my head. Not sure if it's a good idea to go this way at all, but should work. Suppose the table is t and the column is c:

我脑子里想到的第一件事。不确定这样做是否是个好主意,但应该行得通。假设表为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

选择t1。c+1与t的差值为t1左外连接t为t2 (t1.c+1=t2.c) t2。c为空阶,由间隙ASC极限1。

Edit: This one may be a tick faster (and shorter!):

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

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

选择min(t1.c)+1作为间隙,从t到t1左外连接t为t2 (t1.c+1=t2.c)。c是零

#4


6  

This works in SQL Server - can't test it in other systems but it seems standard...

这在SQL Server中工作——不能在其他系统中测试它,但是它看起来很标准……

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1))

You could also add a starting point to the where clause...

您还可以在where子句中添加一个起点……

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1)) AND ID > 2000

So if you had 2000, 2001, 2002, and 2005 where 2003 and 2004 didn't exist, it would return 2003.

如果你有2000年,2001年,2002年,2005年,2003年和2004年都不存在,它会返回2003年。

#5


4  

There isn't really an extremely standard SQL way to do this, but with some form of limiting clause you can do

实际上并没有一种非常标准的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

(MySQL, PostgreSQL)

(MySQL、PostgreSQL)

or

SELECT TOP 1 `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL

(SQL Server)

(SQL Server)

or

SELECT `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
AND ROWNUM = 1

(Oracle)

(Oracle)

#6


2  

Inner join to a view or sequence that has a all possible values.

具有所有可能值的视图或序列的内部连接。

No table? Make a table. I always keep a dummy table around just for this.

没有桌子吗?做一个表。我总是在这里放一个假桌子。

create table artificial_range( 
  id int not null primary key auto_increment, 
  name varchar( 20 ) null ) ;

-- or whatever your database requires for an auto increment column

insert into artificial_range( name ) values ( null )
-- create one row.

insert into artificial_range( name ) select name from artificial_range;
-- you now have two rows

insert into artificial_range( name ) select name from artificial_range;
-- you now have four rows

insert into artificial_range( name ) select name from artificial_range;
-- you now have eight rows

--etc.

insert into artificial_range( name ) select name from artificial_range;
-- you now have 1024 rows, with ids 1-1024

Then,

然后,

 select a.id from artificial_range a
 where not exists ( select * from your_table b
 where b.counter = a.id) ;

#7


1  

My guess:

我的猜测:

SELECT MIN(p1.field) + 1 as gap
FROM table1 AS p1  
INNER JOIN table1 as p3 ON (p1.field = p3.field + 2)
LEFT OUTER JOIN table1 AS p2 ON (p1.field = p2.field + 1)
WHERE p2.field is null;

#8


1  

For PostgreSQL

为PostgreSQL

An example that makes use of recursive query.

一个使用递归查询的例子。

This might be useful if you want to find a gap in a specific range (it will work even if the table is empty, whereas the other examples will not)

如果您希望在特定范围内找到空白,这可能是有用的(即使表是空的,它也能工作,而其他示例则不行)

WITH    
    RECURSIVE a(id) AS (VALUES (1) UNION ALL SELECT id + 1 FROM a WHERE id < 100), -- range 1..100  
    b AS (SELECT id FROM my_table) -- your table ID list    
SELECT a.id -- find numbers from the range that do not exist in main table
FROM a
LEFT JOIN b ON b.id = a.id
WHERE b.id IS NULL
-- LIMIT 1 -- uncomment if only the first value is needed

#9


0  

This one accounts for everything mentioned so far. It includes 0 as a starting point, which it will default to if no values exist as well. I also added the appropriate locations for the other parts of a multi-value key. This has only been tested on SQL Server.

这一点解释了到目前为止所提到的一切。它包含0作为起始点,如果不存在任何值,它将默认为。我还为多值键的其他部分添加了适当的位置。这只在SQL Server上进行了测试。

select
    MIN(ID)
from (
    select
        0 ID
    union all
    select
        [YourIdColumn]+1
    from
        [YourTable]
    where
        --Filter the rest of your key--
    ) foo
left join
    [YourTable]
    on [YourIdColumn]=ID
    and --Filter the rest of your key--
where
    [YourIdColumn] is null

#10


0  

select min([ColumnName]) from [TableName]
where [ColumnName]-1 not in (select [ColumnName] from [TableName])
and [ColumnName] <> (select min([ColumnName]) from [TableName])

#11


0  

Here is standard a SQL solution that runs on all database servers with no change:

下面是一个标准的SQL解决方案,它可以在所有数据库服务器上运行,并且不需要更改:

select min(counter + 1) FIRST_GAP
    from my_table a
    where not exists (select 'x' from my_table b where b.counter = a.counter + 1)
        and a.counter <> (select max(c.counter) from my_table c);

See in action for;

看到在行动;

#12


0  

It works for empty tables or with negatives values as well. Just tested in SQL Server 2012

它适用于空表或带负值的表。刚刚在SQL Server 2012中测试过

 select min(n) from (
select  case when lead(i,1,0) over(order by i)>i+1 then i+1 else null end n from MyTable) w

#13


0  

If You use Firebird 3 this is most elegant and simple:

如果您使用Firebird 3,这是最优雅和简单的:

select RowID
  from (
    select `ID_Column`, Row_Number() over(order by `ID_Column`) as RowID
      from `Your_Table`
        order by `ID_Column`)
    where `ID_Column` <> RowID
    rows 1