127

SQL テーブルのカウンター列の最初の「ギャップ」を見つけたいと思います。たとえば、値が 1、2、4、5 の場合、3 を見つけたいと思います。

もちろん、値を順番に取得して手動で処理することもできますが、SQL でそれを行う方法があるかどうか知りたいです。

さらに、さまざまな DBMS で動作する、非常に標準的な SQL である必要があります。

4

21 に答える 21

214

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

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

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(どこでも機能し、効率が最も低い):

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

スライドウィンドウ関数をサポートするシステム:

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
于 2009-08-21T14:01:51.480 に答える
14

最初の値が 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) 
于 2012-09-12T13:33:23.640 に答える
10

これを行うための非常に標準的な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)

また

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

(SQLサーバー)

また

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)

于 2009-08-21T14:04:27.000 に答える
9

私の頭に浮かんだ最初のこと。このようにするのが良いかどうかはわかりませんが、うまくいくはずです。テーブルが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

編集:これは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
于 2009-08-21T14:01:51.353 に答える
6

これはSQLServerで機能します-他のシステムではテストできませんが、標準のようです...

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

where句に開始点を追加することもできます...

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

したがって、2003と2004が存在しなかった2000、2001、2002、および2005があった場合、2003が返されます。

于 2009-08-21T14:02:12.167 に答える
2

すべての可能な値を持つビューまたはシーケンスへの内部結合。

テーブルがありませんか?テーブルを作ります。私はいつもこのためだけにダミーのテーブルを置いています。

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

それで、

 select a.id from artificial_range a
 where not exists ( select * from your_table b
 where b.counter = a.id) ;
于 2009-08-21T14:02:58.280 に答える
1

私の推測:

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;
于 2009-08-21T14:04:15.080 に答える
1

簡単にできる方法を書いてみました。これが最も効率的かどうかはわかりませんが、仕事は完了します。ギャップを示すのではなく、ギャップの前後の ID を通知することに注意してください (ギャップは複数の値になる可能性があることに注意してください。たとえば、1,2,4,7,11 など)。

例としてsqliteを使用しています

これがテーブル構造の場合

create table sequential(id int not null, name varchar(10) null);

これらはあなたの行です

id|name
1|one
2|two
4|four
5|five
9|nine

クエリは

select a.* from sequential a left join sequential b on a.id = b.id + 1 where b.id is null and a.id <> (select min(id) from sequential)
union
select a.* from sequential a left join sequential b on a.id = b.id - 1 where b.id is null and a.id <> (select max(id) from sequential);

https://gist.github.com/wkimeria/7787ffe84d1c54216f1b320996b17b7e

于 2019-03-25T21:28:38.123 に答える
0
select min([ColumnName]) from [TableName]
where [ColumnName]-1 not in (select [ColumnName] from [TableName])
and [ColumnName] <> (select min([ColumnName]) from [TableName])
于 2016-07-16T10:51:33.970 に答える
0

ほとんどのアプローチは、非常に遅く実行されることがわかりましたmysql。これが私の解決策ですmysql < 8.0。1M レコードでテストされ、終了間際に約 1 秒のギャップがあります。他の SQL フレーバーに適合するかどうかはわかりません。

SELECT cardNumber - 1
FROM
    (SELECT @row_number := 0) as t,
    (
        SELECT (@row_number:=@row_number+1), cardNumber, cardNumber-@row_number AS diff
        FROM cards
        ORDER BY cardNumber
    ) as x
WHERE diff >= 1
LIMIT 0,1
シーケンスは `1` から始まると仮定します。
于 2019-01-10T12:44:57.987 に答える