Du ønsker ikke at se på dual
overhovedet her; bestemt ikke forsøger at indsætte. Du skal spore de højeste og laveste værdier, du har set, mens du itererer gennem løkken. baseret på nogle af elementerne i ename
repræsenterer datoer Jeg er ret sikker på, at du vil have alle dine matches til at være 0-9
, ikke 1-9
. Du refererer også til markørnavnet, når du får adgang til dets felter, i stedet for postvariabelnavnet:
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;
Med værdier i tabellen emp-1111_14_01_01_1111_G1
og emp-1115_14_02_02_1111_G1
, der rapporterer max_seq 1115 min_seq 1111
.
Hvis du virkelig ville involvere dual, kunne du gøre dette inde i løkken i stedet for if/then/assign-mønsteret, men det er ikke nødvendigt:
select least(min_seq, v_seq), greatest(max_seq, v_seq)
into min_seq, max_seq
from dual;
Jeg aner ikke, hvad proceduren kommer til at gøre; der ser ikke ud til at være nogen sammenhæng mellem det, du har i test1
og de værdier, du finder.
Du behøver dog ikke nogen PL/SQL til dette. Du kan få min/max værdierne fra en simpel forespørgsel:
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
Og du kan bruge dem til at generere en liste over alle værdier i dette område:
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
Og et lidt anderledes almindeligt tabeludtryk for at se, hvilke af dem der ikke findes i din tabel, som jeg tror er, hvad du leder efter:
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
eller hvis du foretrækker det:
...
select v.seq as missing_seq
from v
where not exists (select 1 from t where t.seq = v.seq)
order by v.seq;
Baseret på kommentarer tror jeg, at du vil have de manglende værdier for sekvensen for hver kombination af de andre elementer i ID'et (ÅÅ_MM_DD). Dette vil give dig denne opdeling:
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;
Med output som:
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
Hvis du vil lede efter en bestemt dato, koldfiltrerer du den (enten i t
, eller den første gren i r
), men du kan også ændre regex-mønsteret til at inkludere de faste værdier; så for at se efter 14 06
mønsteret ville være 'emp[-][0-9]{4}_14_06_[0-9]{2}[_][0-9]{4}[_][G][1]'
, for eksempel. Det er dog sværere at generalisere, så et filter (where t.yy = '14' and t.mm = '06'
kan være mere fleksibel.
Hvis du insisterer på at have dette i en procedure, kan du gøre datoelementerne valgfrie og ændre regex-mønsteret:
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;
/
Jeg ved ikke, hvorfor du insisterer på, at det skal gøres sådan, eller hvorfor du vil bruge dbms_output
da du stoler på, at klienten/opkalderen viser det; hvad vil dit job gøre med outputtet? Du kan gøre dette til at returnere en sys_refcursor
hvilket ville være mere fleksibelt. men alligevel kan du kalde det sådan her fra SQL*Plus/SQL Developer:
set serveroutput on
exec show_missing_seqs(yy => '14', mm => '01');
anonymous block completed
1112
1113
1114