如何在使用 SQL 运行计数器时找到“差距”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1312101/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-01 03:18:06  来源:igfitidea点击:

How do I find a "gap" in running counter with SQL?

sqlgaps-and-islands

提问by Touko

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。

回答by Quassnoi

In MySQLand PostgreSQL:

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

In SQL Server:

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

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

回答by Ruben

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) 

回答by Michael Krelin - hacker

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 tand 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

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

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

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

回答by chaos

There isn't really an extremelystandard 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 服务器)

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)

(甲骨文)

回答by Mayo

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。

回答by wwmbes

The following solution:

以下解决方案:

  • provides test data;
  • an inner query that produces other gaps; and
  • it works in SQL Server 2012.
  • 提供测试数据;
  • 产生其他差距的内部查询;和
  • 它适用于 SQL Server 2012。

Numbers the ordered rows sequentially in the "with" clause and then reuses the result twice with an inner join on the row number, but offset by 1 so as to compare the row before with the row after, looking for IDs with a gap greater than 1. More than asked for but more widely applicable.

在 " with" 子句中按顺序对有序行进行编号,然后使用行号的内部联接重用结果两次,但偏移 1 以便比较之前的行和之后的行,查找间隔大于的 ID 1. 要求更高,但适用范围更广。

create table #ID ( id integer );

insert into #ID values (1),(2),    (4),(5),(6),(7),(8),    (12),(13),(14),(15);

with Source as (
    select
         row_number()over ( order by A.id ) as seq
        ,A.id                               as id
    from #ID as A WITH(NOLOCK)
)
Select top 1 gap_start from (
    Select 
         (J.id+1) as gap_start
        ,(K.id-1) as gap_end
    from       Source as J
    inner join Source as K
    on (J.seq+1) = K.seq
    where (J.id - (K.id-1)) <> 0
) as G

The inner query produces:

内部查询产生:

gap_start   gap_end

3           3

9           11

The outer query produces:

外部查询产生:

gap_start

3

回答by AlexM

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

回答by tpdi

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) ;

回答by Leonel Martins

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;

回答by Carter Medlin

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 作为起点,如果不存在任何值,它将默认为 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