SQL テーブルのカウンター列の最初の「ギャップ」を見つけたいと思います。たとえば、値が 1、2、4、5 の場合、3 を見つけたいと思います。
もちろん、値を順番に取得して手動で処理することもできますが、SQL でそれを行う方法があるかどうか知りたいです。
さらに、さまざまな DBMS で動作する、非常に標準的な SQL である必要があります。
SQL テーブルのカウンター列の最初の「ギャップ」を見つけたいと思います。たとえば、値が 1、2、4、5 の場合、3 を見つけたいと思います。
もちろん、値を順番に取得して手動で処理することもできますが、SQL でそれを行う方法があるかどうか知りたいです。
さらに、さまざまな DBMS で動作する、非常に標準的な SQL である必要があります。
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
で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
最初の値が 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)
これを行うための非常に標準的な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)
私の頭に浮かんだ最初のこと。このようにするのが良いかどうかはわかりませんが、うまくいくはずです。テーブルが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
これは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が返されます。
すべての可能な値を持つビューまたはシーケンスへの内部結合。
テーブルがありませんか?テーブルを作ります。私はいつもこのためだけにダミーのテーブルを置いています。
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) ;
私の推測:
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;
簡単にできる方法を書いてみました。これが最も効率的かどうかはわかりませんが、仕事は完了します。ギャップを示すのではなく、ギャップの前後の 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
select min([ColumnName]) from [TableName]
where [ColumnName]-1 not in (select [ColumnName] from [TableName])
and [ColumnName] <> (select min([ColumnName]) from [TableName])
ほとんどのアプローチは、非常に遅く実行されることがわかりました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` から始まると仮定します。