Oracle
 sql >> Base de données >  >> RDS >> Oracle

trouver les numéros manquants de la séquence après avoir obtenu la séquence d'une chaîne?

Vous ne voulez pas regarder dual du tout ici; certainement pas essayer d'insérer. Vous devez suivre les valeurs les plus élevées et les plus basses que vous avez vues pendant que vous parcourez la boucle. basé sur certains des éléments de ename représentant des dates, je suis sûr que vous voulez que toutes vos correspondances soient 0-9 , pas 1-9 . Vous faites également référence au nom du curseur lorsque vous accédez à ses champs, au lieu du nom de la variable d'enregistrement :

  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;

Avec des valeurs dans le tableau de emp-1111_14_01_01_1111_G1 et emp-1115_14_02_02_1111_G1 , qui rapporte max_seq 1115 min_seq 1111 .

Si vous vouliez vraiment impliquer dual, vous pourriez le faire à l'intérieur de la boucle, au lieu du modèle if/then/assign, mais ce n'est pas nécessaire :

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

Je n'ai aucune idée de ce que la procédure va faire; il semble n'y avoir aucune relation entre ce que vous avez dans test1 et les valeurs que vous trouvez.

Vous n'avez cependant pas besoin de PL/SQL pour cela. Vous pouvez obtenir les valeurs min/max à partir d'une simple requête :

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 

Et vous pouvez les utiliser pour générer une liste de toutes les valeurs de cette plage :

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 

Et une expression de table commune légèrement différente pour voir laquelle de celles-ci n'existe pas dans votre table, ce qui, je pense, est ce que vous recherchez :

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 

ou si vous préférez :

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

Sur la base des commentaires, je pense que vous voulez les valeurs manquantes pour la séquence pour chaque combinaison des autres éléments de l'ID (YY_MM_DD). Cela vous donnera cette répartition :

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;

Avec une sortie comme :

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

Si vous souhaitez rechercher une date en particulier, vous filtrez celle-ci à froid (soit en t , ou la première branche de r ), mais vous pouvez également modifier le modèle regex pour inclure les valeurs fixes ; donc chercher 14 06 le modèle serait 'emp[-][0-9]{4}_14_06_[0-9]{2}[_][0-9]{4}[_][G][1]' , par exemple. C'est plus difficile à généraliser cependant, donc un filtre (where t.yy = '14' and t.mm = '06' pourrait être plus flexible.

Si vous insistez pour l'avoir dans une procédure, vous pouvez rendre les éléments de date facultatifs et modifier le modèle de regex :

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

Je ne sais pas pourquoi vous insistez pour que cela soit fait comme ça ou pourquoi vous voulez utiliser dbms_output car vous comptez sur le client/appelant affichant cela ; qu'est-ce que votre travail va faire avec la sortie ? Vous pouvez faire en sorte que ce retour soit un sys_refcursor ce qui serait plus souple. mais quoi qu'il en soit, vous pouvez l'appeler ainsi depuis SQL*Plus/SQL Developer :

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

anonymous block completed
1112
1113
1114