134

値が別のシステムからインポートされたテーブルを持つデータベースがあります。自動インクリメント列があり、重複する値はありませんが、欠損値があります。たとえば、次のクエリを実行します。

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などを返却したいです。

これは可能ですか?

4

14 に答える 14

185

アップデート

ConfexianMJS は、パフォーマンスに関してはるかに優れ た回答を提供しました。

(できるだけ速くない)答え

これは、任意のサイズのテーブル (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- 現在のギャップの最初の ID
  • gap_ends_at- 現在のギャップの最後の ID
于 2011-05-19T11:05:31.503 に答える
135

これは、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 でさえあるかもしれません。

于 2015-04-19T22:32:31.137 に答える
12

トリックを実行する必要がある迅速でダーティなクエリ:

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_idmissing_inbetween
 1 4 2
68 70 1
75 87 11
于 2010-12-02T23:07:34.683 に答える
2

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
于 2010-12-02T22:54:35.113 に答える
1

上記の 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
于 2015-02-17T12:31:06.927 に答える
1

これらはすべて機能しているように見えますが、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;
于 2014-04-04T15:09:25.530 に答える
1

さまざまな方法で試してみたところ、次の単純なクエリで最高のパフォーマンスが得られました。

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つの左結合。次が見つからない場合にのみ、サブクエリは存在する次のIDを見つけてギャップの終わりを見つけます。等号(=)を使用したクエリは、大なり(>)演算子よりもパフォーマンスが優れているため、これを行いました。

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を使用して、クエリを表示およびテストできます。

http://sqlfiddle.com/#!9/6bdca7/1

于 2019-02-09T19:24:49.020 に答える