別のシステムから値がインポートされたテーブルを持つデータベースがあります。自動インクリメント列があり、重複する値はありませんが、欠損値があります。たとえば、次のクエリを実行すると:
select count(id) from arrc_vouchers where id between 1 and 100
100を返す必要がありますが、代わりに87を返します。不足している数値の値を返すクエリを実行できますか?たとえば、ID 1〜70および83〜100のレコードが存在する場合がありますが、IDが71〜82のレコードはありません。 71、72、73などを返したい.
これは可能ですか?
ConfexianMJSが提供するmuch betteranswer パフォーマンスの点で。
これは、(100行だけでなく)任意のサイズのテーブルで機能するバージョンです。
SELECT (t1.id + 1) as gap_starts_at,
(SELECT MIN(t3.id) -1 FROM arrc_vouchers t3 WHERE t3.id > t1.id) as gap_ends_at
FROM arrc_vouchers t1
WHERE NOT EXISTS (SELECT t2.id FROM arrc_vouchers t2 WHERE t2.id = t1.id + 1)
HAVING gap_ends_at IS NOT NULL
gap_starts_at
-現在のギャップの最初のIDgap_ends_at
-現在のギャップの最後のIDこれはちょうど80k以上の行を持つテーブルのギャップを見つけるのに役立ちました:
SELECT
CONCAT(z.expected, IF(z.got-1>z.expected, CONCAT(' thru ',z.got-1), '')) AS missing
FROM (
SELECT
@rownum:=@rownum+1 AS expected,
IF(@rownum=YourCol, 0, @rownum:=YourCol) AS got
FROM
(SELECT @rownum:=0) AS a
JOIN YourTable
ORDER BY YourCol
) AS z
WHERE z.got!=0;
結果:
+------------------+
| missing |
+------------------+
| 1 thru 99 |
| 666 thru 667 |
| 50000 |
| 66419 thru 66456 |
+------------------+
4 rows in set (0.06 sec)
列expected
とgot
の順序が重要であることに注意してください。
YourCol
が1から始まっておらず、それが重要でないことがわかっている場合は、置き換えることができます
(SELECT @rownum:=0) AS a
と
(SELECT @rownum:=(SELECT MIN(YourCol)-1 FROM YourTable)) AS a
新しい結果:
+------------------+
| missing |
+------------------+
| 666 thru 667 |
| 50000 |
| 66419 thru 66456 |
+------------------+
3 rows in set (0.06 sec)
欠落しているIDに対して何らかの種類のシェルスクリプトタスクを実行する必要がある場合は、このバリアントを使用して、bashで反復できる式を直接生成することもできます。
SELECT GROUP_CONCAT(IF(z.got-1>z.expected, CONCAT('$(',z.expected,' ',z.got-1,')'), z.expected) SEPARATOR " ") AS missing
FROM ( SELECT @rownum:=@rownum+1 AS expected, IF(@rownum=height, 0, @rownum:=height) AS got FROM (SELECT @rownum:=0) AS a JOIN block ORDER BY height ) AS z WHERE z.got!=0;
これは次のような出力を生成します
$(seq 1 99) $(seq 666 667) 50000 $(seq 66419 66456)
次に、それをコピーしてbashターミナルのforループに貼り付け、すべてのIDに対してコマンドを実行できます。
for ID in $(seq 1 99) $(seq 666 667) 50000 $(seq 66419 66456); do
echo $ID
# fill the gaps
done
上記と同じですが、読み取りと実行の両方が可能です。上記の「CONCAT」コマンドを変更することにより、他のプログラミング言語の構文を生成できます。または多分SQL。
トリックを実行する必要がある迅速でダーティなクエリ:
SELECT a AS id, b AS next_id, (b - a) -1 AS missing_inbetween
FROM
(
SELECT a1.id AS a , MIN(a2.id) AS b
FROM arrc_vouchers AS a1
LEFT JOIN arrc_vouchers AS a2 ON a2.id > a1.id
WHERE a1.id <= 100
GROUP BY a1.id
) AS tab
WHERE
b > a + 1
これにより、上に欠落しているIDを持つID、存在するnext_id、およびその間に欠落しているIDを示すテーブルが表示されます。
id next_id missing_inbetween 1 4 2 68 70 1 75 87 11
MariaDB
を使用している場合、 シーケンスストレージエンジン を使用した高速(800%)オプションがあります。
SELECT * FROM seq_1_to_50000 WHERE SEQ NOT IN (SELECT COL FROM TABLE);
クエリ+何らかの処理を行うコードを必要とする代替ソリューションは次のようになります。
select l.id lValue, c.id cValue, r.id rValue
from
arrc_vouchers l
right join arrc_vouchers c on l.id=IF(c.id > 0, c.id-1, null)
left join arrc_vouchers r on r.id=c.id+1
where 1=1
and c.id > 0
and (l.id is null or r.id is null)
order by c.id asc;
クエリには、MySQLのプランナによってパフォーマンスが処理されないことがわかっている副選択が含まれていないことに注意してください。
これは、小さい値(lValue)または大きい値(rValue)を持たないcentralValue(cValue)ごとに1つのエントリを返します。つまり:
lValue |cValue|rValue
-------+------+-------
{null} | 2 | 3
8 | 9 | {null}
{null} | 22 | 23
23 | 24 | {null}
{null} | 29 | {null}
{null} | 33 | {null}
詳細に進むことなく(次の段落で説明します)、この出力は次のことを意味します。
したがって、基本的な考え方は、値ごとに隣接値があるかどうかを確認して、同じテーブルで右および左結合を実行することです(つまり、中央値が「3」の場合、左で3-1 = 2と3 + 1 =右)、ROWの右または左にNULL値がある場合、隣接する値がないことがわかります。
私のテーブルの完全な生の出力は次のとおりです。
select * from arrc_vouchers order by id asc;
0
2
3
4
5
6
7
8
9
22
23
24
29
33
いくつかのメモ:
100行と1〜100の値を含む1列の一時テーブルを作成します。
外部このテーブルをarrc_vouchersテーブルに結合し、arrc_vouchers idがnullである単一の列値を選択します。
このブラインドをコーディングしますが、動作するはずです。
select tempid from temptable
left join arrc_vouchers on temptable.tempid = arrc_vouchers.id
where arrc_vouchers.id is null
2つの数値の間に最大1つのギャップがあるシーケンス(1,3,5,6など)がある場合、使用できるクエリは次のとおりです。
select s.id+1 from source1 s where s.id+1 not in(select id from source1) and s.id+1<(select max(id) from source1);
source1
id
lucekによる上記の回答に基づいて、このストアドプロシージャを使用すると、非連続レコードを見つけるためにテストするテーブル名と列名を指定できます。したがって、元の質問に答え、@ varを使用してテーブルを表す方法を示します。ストアドプロシージャの列。
create definer=`root`@`localhost` procedure `spfindnoncontiguous`(in `param_tbl` varchar(64), in `param_col` varchar(64))
language sql
not deterministic
contains sql
sql security definer
comment ''
begin
declare strsql varchar(1000);
declare tbl varchar(64);
declare col varchar(64);
set @tbl=cast(param_tbl as char character set utf8);
set @col=cast(param_col as char character set utf8);
set @strsql=concat("select
( t1.",@col," + 1 ) as starts_at,
( select min(t3.",@col,") -1 from ",@tbl," t3 where t3.",@col," > t1.",@col," ) as ends_at
from ",@tbl," t1
where not exists ( select t2.",@col," from ",@tbl," t2 where t2.",@col," = t1.",@col," + 1 )
having ends_at is not null");
prepare stmt from @strsql;
execute stmt;
deallocate prepare stmt;
end
これらはすべて機能しているように見えますが、50,000件のレコードがある場合、結果セットは非常に長い時間で戻ります。
私はこれを使用し、クエリからのリターンがはるかに速いギャップまたは次に使用可能なもの(最後に使用したもの+ 1)を見つけました。
SELECT a.id as beforegap, a.id+1 as avail
FROM table_name a
where (select b.id from table_name b where b.id=a.id+1) is null
limit 1;
シリーズの生成を使用して、1からテーブルの最大IDまでの数値を生成できます。次に、このシリーズにないidでクエリを実行します。
おそらく関連性はありませんが、一連の数字のギャップをリストするためにこのようなものを探していましたが、探しているものに応じて複数の異なるソリューションを持つこの投稿を見つけました。私はシーケンスで最初に利用可能なギャップ(つまり、次に利用可能な数)を探していましたが、これはうまくいくようです。
SELECT MIN(l.number_sequence + 1)as nextavabile as patient as l LEFT OUTER JOIN patient as r on l.number_sequence + 1 = r.number_sequence WHERE r.number_sequenceはNULLです。そこで議論された他のいくつかのシナリオとソリューションは2005年から!
I 試行 さまざまな方法で、私が見つけた最高のパフォーマンスはこの単純なクエリでした:
select a.id+1 gapIni
,(select x.id-1 from arrc_vouchers x where x.id>a.id+1 limit 1) gapEnd
from arrc_vouchers a
left join arrc_vouchers b on b.id=a.id+1
where b.id is null
order by 1
;
...次のidが存在するかどうかを確認するための1つの左結合、next ifが見つからない場合にのみ、サブクエリは存在する次のIDを見つけるギャップの終わりを見つけます。 equal(=)を使用したクエリはより大きい(>)演算子。
sqlfiddle を使用すると、他のクエリのパフォーマンスはそれほど異なりませんが、実際のデータベースでは、上記のクエリは他のクエリより3倍高速です。
スキーマ:
CREATE TABLE arrc_vouchers (id int primary key)
;
INSERT INTO `arrc_vouchers` (`id`) VALUES (1),(4),(5),(7),(8),(9),(10),(11),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29)
;
パフォーマンスを比較するために、以下のすべてのクエリに従ってください。
select a.id+1 gapIni
,(select x.id-1 from arrc_vouchers x where x.id>a.id+1 limit 1) gapEnd
from arrc_vouchers a
left join arrc_vouchers b on b.id=a.id+1
where b.id is null
order by 1
;
select *, (gapEnd-gapIni) qt
from (
select id+1 gapIni
,(select x.id from arrc_vouchers x where x.id>a.id limit 1) gapEnd
from arrc_vouchers a
order by id
) a where gapEnd <> gapIni
;
select id+1 gapIni
,(select x.id from arrc_vouchers x where x.id>a.id limit 1) gapEnd
#,coalesce((select id from arrc_vouchers x where x.id=a.id+1),(select x.id from arrc_vouchers x where x.id>a.id limit 1)) gapEnd
from arrc_vouchers a
where id+1 <> (select x.id from arrc_vouchers x where x.id>a.id limit 1)
order by id
;
select id+1 gapIni
,coalesce((select id from arrc_vouchers x where x.id=a.id+1),(select x.id from arrc_vouchers x where x.id>a.id limit 1)) gapEnd
from arrc_vouchers a
order by id
;
select id+1 gapIni
,coalesce((select id from arrc_vouchers x where x.id=a.id+1),concat('*** GAT *** ',(select x.id from arrc_vouchers x where x.id>a.id limit 1))) gapEnd
from arrc_vouchers a
order by id
;
たぶんそれは誰かを助け、役に立つでしょう。
この sqlfiddle を使用して、クエリを表示およびテストできます。