Generate numbers in natural order, and custom order
select 'E' || trim( to_char(nvl(a, 0), '00000000') ) customer_id-- fixed-digit fill
from (select t.a, t1.a b
from (select rownum a
from dual
connect by rownum <=
(select max(to_number(replace(customer_id, 'E', ''))) + 2
from tb_cus_firm
where customer_id like 'E%')) t,
(select to_number(replace(customer_id, 'E', '')) a
from tb_cus_firm
where customer_id like 'E%') t1
where t.a not like '%4%' and t.a = t1.a(+))
where b is null and rownum = 1
Parse:
select a,rownum
from (select t.a, t1.a b
from (select rownum a
from dual
connect by rownum <=
(select max(to_number(replace(customer_id, 'E', ''))) + 2
from tb_cus_firm
where customer_id like 'E%')) t,
(select to_number(replace(customer_id, 'E', '')) a
from tb_cus_firm
where customer_id like 'E%') t1
where t.a not like '%4%' and t.a = t1.a(+))
/*where b is null*/ /*and rownum = 1*/ rownum is changed in the scope according to the current result set, this sentence ensures that the sequence that is not used after skipping starts
Same two-column comparison: take the first one that is theoretically compared to those that are actually null