Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Finden Sie fehlende Zahlen aus der Sequenz, nachdem Sie die Sequenz aus einer Zeichenfolge erhalten haben?

Sie möchten nicht dual sehen überhaupt hier; sicherlich nicht versuchen, einzufügen. Sie müssen die höchsten und niedrigsten Werte verfolgen, die Sie beim Durchlaufen der Schleife gesehen haben. basierend auf einigen Elementen von ename Datumsangaben darstellen Ich bin mir ziemlich sicher, dass alle Ihre Übereinstimmungen 0-9 sein sollen , nicht 1-9 . Sie beziehen sich auch auf den Cursornamen, wenn Sie auf seine Felder zugreifen, anstatt auf den Namen der Datensatzvariablen:

  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;

Mit Werten in der Tabelle von emp-1111_14_01_01_1111_G1 und emp-1115_14_02_02_1111_G1 , das max_seq 1115 min_seq 1111 meldet .

Wenn Sie dual verwenden möchten, können Sie dies innerhalb der Schleife anstelle des if/then/assign-Musters tun, aber es ist nicht notwendig:

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

Ich habe keine Ahnung, was das Verfahren bewirken wird; Es scheint keine Beziehung zwischen dem zu geben, was Sie in test1 haben und die Werte, die Sie finden.

Sie benötigen dafür jedoch kein PL/SQL. Sie können die Min/Max-Werte aus einer einfachen Abfrage erhalten:

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 

Und Sie können diese verwenden, um eine Liste aller Werte in diesem Bereich zu erstellen:

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 

Und ein etwas anderer allgemeiner Tabellenausdruck, um zu sehen, welche davon nicht in Ihrer Tabelle vorhanden sind, was meiner Meinung nach das ist, wonach Sie suchen:

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 

oder wenn Sie es vorziehen:

...
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-Geige .

Basierend auf Kommentaren denke ich, dass Sie die fehlenden Werte für die Sequenz für jede Kombination der anderen Elemente der ID (YY_MM_DD) wünschen. Dadurch erhalten Sie diese Aufschlüsselung:

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;

Mit Ausgabe wie:

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-Geige .

Wenn Sie nach einem bestimmten Datum suchen möchten, können Sie dieses kalt filtern (entweder in t , oder die erste Verzweigung in r ), aber Sie könnten das Regex-Muster auch so ändern, dass es die festen Werte enthält; Suchen Sie also nach 14 06 das Muster wäre 'emp[-][0-9]{4}_14_06_[0-9]{2}[_][0-9]{4}[_][G][1]' , zum Beispiel. Das ist jedoch schwieriger zu verallgemeinern, also ein Filter (where t.yy = '14' and t.mm = '06' könnte flexibler sein.

Wenn Sie darauf bestehen, dies in einer Prozedur zu haben, können Sie die Datumselemente optional machen und das Regex-Muster ändern:

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;
/

Ich weiß nicht, warum Sie darauf bestehen, dass es so gemacht werden muss oder warum Sie dbms_output verwenden möchten da Sie sich darauf verlassen, dass der Client / Anrufer dies anzeigt; Was wird Ihr Job mit dem Output machen? Sie könnten dafür sorgen, dass dies einen sys_refcursor zurückgibt was flexibler wäre. aber wie auch immer, Sie können es von SQL*Plus/SQL Developer so aufrufen:

set serveroutput on
exec show_missing_seqs(yy => '14', mm => '01');

anonymous block completed
1112
1113
1114