Procedure para gerar sequencial numérico e/ou alfabético
Autor: Alex Nascimento | Publicado: 2012-01-29T17:06:00-08:00

Pessoal, sei que já faz bastante tempo que não posto alguma novidade no blog. Mas hoje vou compartilhar uma procedure que pode ser útil para geração de sequenciais que deve-se considerar caracteres numéricos e do alfabeto (0-9, A-Z e a-z).

Um exemplo de utilidade, seria gerar uma faixa assim: A01 - A10. Essa procedure lhe retornará A01, A02, A03, A04, A05, A06, A07, A08, A09 e A10, por exemplo.

Vamos ao código fonte da nossa procedure então... os comentários existentes na procedure explicam a lógica e as validações existentes...

create or replace procedure PR_GERASEQUENCIALNS(p_seqIni varchar2,
                                                      p_seqFin varchar2,
                                                      p_considerarAlpha number) is

   v_seqIni varchar2(50);
   v_seqFin varchar2(50);
   v_considerarAlpha boolean;
   v_somaNumericosIni number;
   v_somaNumericosFin number;
begin
   --atualiza variáveis virtuais...
   v_seqIni := p_seqIni;
   v_seqFin := p_seqFin;
   if p_considerarAlpha = 1 then
      v_considerarAlpha := true;
   else 
      v_considerarAlpha := false;
   end if;
   
   --preenche com zeros a frente os sequenciais para terem o mesmo tamanho...
   if length(v_seqIni) &gt; length(v_seqFin) then
      v_seqFin := lpad(v_seqFin, length(v_seqFin), '0');
   elsif length(v_seqIni) &lt; length(v_seqFin) then
      v_seqIni := lpad(v_seqIni, length(v_seqIni), '0');
   end if;
   
   --avalia as restrições dos parâmetros para prosseguir...
   --primeira restriçäo: o posicionamento dos caracteres deve ser identico, ou seja, um maisculo deve corresponder ao maisculo.
   --segunda restrição: também deve-se levar em consideração que se o parâmetro p_considerarAlpha for 0 e for enviado A98 - B01, por exemplo, nenhum 
   --sequencial será impresso, pois 
   for i in 1 .. length(v_seqFin) loop
      --avalia se a sequencia inicial pertence a faixa de númericos da tabela ASCII e os sequencial final não..
      if ascii(substr(v_seqIni, i, 1)) between 48 and 57 and ascii(substr(v_seqFin, i, 1)) not between 48 and 57 then
         raise_application_error(-20001,'Existe inconsistência entre os caracteres da posição '||i||'. Reavalie os sequenciais.');   
      --avalia se a sequencia inicial pertence a faixa de alfabeticos maiscula da tabela ASCII e os sequencial final não..         
      elsif v_considerarAlpha and ascii(substr(v_seqIni, i, 1)) between 65 and 90 and ascii(substr(v_seqFin, i, 1)) not between 65 and 90 then
         raise_application_error(-20001,'Existe inconsistência entre os caracteres da posição '||i||'. Reavalie os sequenciais.');
      --avalia se a sequencia inicial pertence a faixa de alfabeticos minusculos da tabela ASCII e os sequencial final não..         
      elsif v_considerarAlpha and ascii(substr(v_seqIni, i, 1)) between 97 and 122 and ascii(substr(v_seqFin, i, 1)) not between 97 and 122 then
         raise_application_error(-20001,'Existe inconsistência entre os caracteres da posição '||i||'. Reavalie os sequenciais.');
      elsif not(v_considerarAlpha) and ascii(substr(v_seqIni, i, 1)) between 48 and 57 then
         v_somaNumericosIni := ascii(substr(v_seqIni, i, 1));
         v_somaNumericosFin := ascii(substr(v_seqFin, i, 1));
      end if;
   end loop;
   
   if not(v_considerarAlpha) and v_somaNumericosIni &gt; v_somaNumericosFin then
      raise_application_error(-20001,'A sequencia inicial é maior que a final. Faça uma reavaliação.');
   end if;

   --gera o sequencial de numeros de série...
   for i in 1 .. length(v_seqFin) loop
      if (not(v_considerarAlpha) and ascii(substr(v_seqIni, i, 1)) between 48 and 57) or v_considerarAlpha then
         while ascii(substr(v_seqIni, i, 1)) &lt; ascii(substr(v_seqFin, i, 1)) loop
               dbms_output.put_line(v_seqIni);
      
               v_seqIni := FC_INCSERIAL(v_seqIni);
         end loop;
      end if;
   end loop;
   
   dbms_output.put_line(v_seqFin);
end;

Como todos podem ver, para compilar a procedure com êxito, será necessário ter a função FC_INCSERIAL. Então vou compartilhá-la ela em seguida...

create or replace function FC_INCSERIAL(p_seq varchar2,
                                        p_posicao number default 0) return varchar2 is
  r_seq varchar2(50);
  v_posicao number default p_posicao;
  v_asciiCorrente number;
begin
  --tratamento para a posição...
  if v_posicao = 0 then
     v_posicao := length(p_seq);
  end if;
  
  --tratamento para o ascii
  v_asciiCorrente := ascii(substr(p_seq,v_posicao,1));
  
  --tratamento para caracteres númericos...
  if v_asciiCorrente between 48 and 57 then
     if v_asciiCorrente = 57 then
        r_seq := FC_INCSERIAL(substr(p_seq,0,v_posicao-1),v_posicao-1);
        r_seq := r_seq||chr(48);
     else
        r_seq := substr(p_seq,0,v_posicao-1)||chr(v_asciiCorrente+1);
     end if;
  --tratamento para caracteres alfabeticos maiusculos...     
  elsif v_asciiCorrente between 65 and 90 then
     if v_asciiCorrente = 57 then
        r_seq := FC_INCSERIAL(substr(p_seq,0,v_posicao-1),v_posicao-1);
        r_seq := r_seq||chr(65);
     else
        r_seq := substr(p_seq,0,v_posicao-1)||chr(v_asciiCorrente+1);
     end if;
  --tratamento para caracteres alfabeticos minusculos...     
  elsif v_asciiCorrente between 97 and 122 then
     if v_asciiCorrente = 57 then
        r_seq := FC_INCSERIAL(substr(p_seq,0,v_posicao-1),v_posicao-1);
        r_seq := r_seq||chr(97);
     else
        r_seq := substr(p_seq,0,v_posicao-1)||chr(v_asciiCorrente+1);
     end if;
  end if;
  
  return(r_seq);
end FC_INCSERIAL;

O que torna esse código efetivo é a função recursiva. Outro detalhe que acredito que seja interessante é a lógica de utilizar a tabela ASCII para fazer o incremento de&nbsp;numéricos&nbsp;e&nbsp;alfabéticos.

Um exemplo de chamada para ela seria assim...

begin
  PR_GERASEQUENCIALNS('A90','B05',1);
end;

O resultado da execução desse exemplo, resultaria no seguinte output:

A90
A91
A92
A93
A94
A95
A96
A97
A98
A99
B00
B01
B02
B03
B04
B05

Bom pessoal, por hoje é isso... espero que gostem desse pequeno post...