sql >> データベース >  >> RDS >> Oracle

文字列からシーケンスを取得した後、シーケンスから欠落している番号を見つけますか?

    dualを見たくない ここではまったく;確かに挿入しようとはしていません。ループを反復処理するときに見た最高値と最低値を追跡する必要があります。 enameのいくつかの要素に基づいています 日付を表すすべての一致を0-9にする必要があると確信しています 、1-9ではありません 。また、フィールドにアクセスするときに、レコード変数名の代わりにカーソル名を参照しています:

      FOR List_ENAME_rec IN List_ENAME_cur loop
        if REGEXP_LIKE(List_ENAME_rec.ENAME,'emp[-][0-9]{4}[_][0-9]{2}[_][0-9]{2}[_][0-9]{2}[_][0-9]{4}[_][G][1]') then 
          V_seq := substr(List_ENAME_rec.ename,5,4);
          V_Year := substr(List_ENAME_rec.ename,10,2);
          V_Month := substr(List_ENAME_rec.ename,13,2);
          V_day := substr(List_ENAME_rec.ename,16,2);
    
          if min_seq is null or V_seq < min_seq then
            min_seq := v_seq;
          end if;
          if max_seq is null or V_seq > max_seq then
            max_seq := v_seq;
          end if;
    
        end if;
      end loop;
    

    emp-1111_14_01_01_1111_G1のテーブルの値を使用 およびemp-1115_14_02_02_1111_G1 、max_seq 1115 min_seq 1111を報告します 。

    本当にデュアルを使用したい場合は、if / then / assignパターンの代わりに、ループ内でこれを行うことができますが、必須ではありません:

          select least(min_seq, v_seq), greatest(max_seq, v_seq)
          into min_seq, max_seq
          from dual;
    

    手順がどうなるかわかりません。 test1で取得したものとの間に関係はないようです そしてあなたが見つけている価値観。

    ただし、これにはPL/SQLは必要ありません。単純なクエリから最小/最大値を取得できます:

    select min(to_number(substr(ename, 5, 4))) as min_seq,
      max(to_number(substr(ename, 5, 4))) as max_seq
    from table1
    where status = 2
    and regexp_like(ename,
      'emp[-][0-9]{4}[_][0-9]{2}[_][0-9]{2}[_][0-9]{2}[_][0-9]{4}[_][G][1]')
    
       MIN_SEQ    MAX_SEQ
    ---------- ----------
          1111       1115 
    

    そして、それらを使用して、その範囲内のすべての値のリストを生成できます。

    with t as (
      select min(to_number(substr(ename, 5, 4))) as min_seq,
        max(to_number(substr(ename, 5, 4))) as max_seq
      from table1
      where status = 2
      and regexp_like(ename,
        'emp[-][0-9]{4}[_][0-9]{2}[_][0-9]{2}[_][0-9]{2}[_][0-9]{4}[_][G][1]')
    )
    select min_seq + level - 1 as seq
    from t
    connect by level <= (max_seq - min_seq) + 1;
    
           SEQ
    ----------
          1111 
          1112 
          1113 
          1114 
          1115 
    

    そして、テーブルに存在しないものを確認するためのわずかに異なる一般的なテーブル式。これがあなたが求めているものだと思います。

    with t as (
      select to_number(substr(ename, 5, 4)) as seq
      from table1
      where status = 2
      and regexp_like(ename,
        'emp[-][0-9]{4}[_][0-9]{2}[_][0-9]{2}[_][0-9]{2}[_][0-9]{4}[_][G][1]')
    ),
    u as (
      select min(seq) as min_seq,
        max(seq) as max_seq
      from t
    ),
    v as (
      select min_seq + level - 1 as seq
      from u
      connect by level <= (max_seq - min_seq) + 1
    )
    select v.seq as missing_seq
    from v
    left join t on t.seq = v.seq
    where t.seq is null
    order by v.seq;
    
    MISSING_SEQ
    -----------
           1112 
           1113 
           1114 
    

    または必要に応じて:

    ...
    select v.seq as missing_seq
    from v
    where not exists (select 1 from t where t.seq = v.seq)
    order by v.seq;
    

    SQLフィドル 。

    コメントに基づいて、IDの他の要素(YY_MM_DD)の各組み合わせのシーケンスに欠落している値が必要だと思います。これにより、その内訳がわかります:

    with t as (
      select to_number(substr(ename, 5, 4)) as seq,
        substr(ename, 10, 2) as yy,
        substr(ename, 13, 2) as mm,
        substr(ename, 16, 2) as dd
      from table1
      where status = 2
      and regexp_like(ename,
        'emp[-][0-9]{4}[_][0-9]{2}[_][0-9]{2}[_][0-9]{2}[_][0-9]{4}[_][G][1]')
    ),
    r (yy, mm, dd, seq, max_seq) as (
      select yy, mm, dd, min(seq), max(seq)
      from t
      group by yy, mm, dd
      union all
      select yy, mm, dd, seq + 1, max_seq
      from r
      where seq + 1 <= max_seq
    )
    select yy, mm, dd, seq as missing_seq
    from r
    where not exists (
      select 1 from t
      where t.yy = r.yy
      and t.mm = r.mm
      and t.dd = r.dd
      and t.seq = r.seq
    )
    order by yy, mm, dd, seq;
    

    次のような出力:

    YY   MM   DD    MISSING_SEQ 
    ---- ---- ---- -------------
    14   01   01            1112 
    14   01   01            1113 
    14   01   01            1114 
    14   02   02            1118 
    14   02   02            1120 
    14   02   03            1127 
    14   02   03            1128 
    

    SQLフィドル 。

    特定の日付を検索する場合は、その日付をコールドフィルタリングします(tのいずれかで) 、またはrの最初のブランチ )、ただし、正規表現パターンを変更して固定値を含めることもできます。したがって、14 06を探します パターンは'emp[-][0-9]{4}_14_06_[0-9]{2}[_][0-9]{4}[_][G][1]' 、 例えば。ただし、これを一般化するのは難しいため、フィルター(where t.yy = '14' and t.mm = '06' より柔軟かもしれません。

    プロシージャでこれを使用することを主張する場合は、日付要素をオプションにして、正規表現パターンを変更できます。

    create or replace procedure show_missing_seqs(yy in varchar2 default '[0-9]{2}',
      mm in varchar2 default '[0-9]{2}', dd in varchar2 default '[0-9]{2}') as
    
      pattern varchar2(80);
      cursor cur (pattern varchar2) is
        with t as (
          select to_number(substr(ename, 5, 4)) as seq,
            substr(ename, 10, 2) as yy,
            substr(ename, 13, 2) as mm,
            substr(ename, 16, 2) as dd
          from table1
          where status = 2
          and regexp_like(ename, pattern)
        ),
        r (yy, mm, dd, seq, max_seq) as (
          select yy, mm, dd, min(seq), max(seq)
          from t
          group by yy, mm, dd
          union all
          select yy, mm, dd, seq + 1, max_seq
          from r
          where seq + 1 <= max_seq
        )
        select yy, mm, dd, seq as missing_seq
        from r
        where not exists (
          select 1 from t
          where t.yy = r.yy
          and t.mm = r.mm
          and t.dd = r.dd
          and t.seq = r.seq
        )
        order by yy, mm, dd, seq;
    begin
      pattern := 'emp[-][0-9]{4}[_]'
        || yy || '[_]' || mm || '[_]' || dd
        || '[_][0-9]{4}[_][G][1]';
      for rec in cur(pattern) loop
        dbms_output.put_line(to_char(rec.missing_seq, 'FM0000'));
      end loop;
    end show_missing_seqs;
    /
    

    なぜこのように行わなければならないと主張するのか、なぜdbms_outputを使用したいのかわかりません あなたはそれを表示するクライアント/発信者に依存しているので;あなたの仕事は出力で何をしますか?これでsys_refcursorを返すことができます これはより柔軟になります。とにかく、SQL * Plus / SQL Developerから次のように呼び出すことができます:

    set serveroutput on
    exec show_missing_seqs(yy => '14', mm => '01');
    
    anonymous block completed
    1112
    1113
    1114
    



    1. INTOFROMを実行するときのMySQLエラー1327

    2. ORA-01843:Oracleに日付を挿入すると有効な月ではありません

    3. ストアドプロシージャを使用する利点と欠点

    4. Re-IDリスクを下げるための間接識別子の匿名化