As expressões regulares fazem parte dos recursos computacionais há algum tempo. O fato de eu atuar fortemente com sistemas financeiros fez com que, por algum tempo, eu desdenhasse tal recurso por nunca ter visto uma aplicação prática para ele. Porém, uma necessidade em especial me motivou a utilizá-lo. Imagine o seguinte cenário:
Eu tenho tabelas no Oracle que contém os dias da semana em que uma determinada cobrança deve ser gerada, que possui o seguinte formato: 2;4;6. Isso significa que esta cobrança deve ser gerada nas segundas, quartas e sextas-feiras
Para que eu possa gerar a cobrança no dia correto eu preciso saber se o dia de hoje se encontra em um dos dias listados. O script abaixo é uma simulação do cenário no Oracle:
set serveroutput on
DECLARE
--------------------------------------------------
--> Array para simular uma tabela
--------------------------------------------------
type r_dias is record (dias varchar2(10));
type t_dias is table of r_dias index by binary_integer;
v_dias t_dias;
--------------------------------------------------
--> Dia corrente
--------------------------------------------------
v_hoje varchar2(1) := TO_CHAR(sysdate,'D');
BEGIN
--------------------------------------------------
--> Alimentando o array
--------------------------------------------------
v_dias(1).dias := '2;4;6';
v_dias(2).dias := '3;5';
v_dias(3).dias := '2;3;4;5;6';
--------------------------------------------------
--> Processando o array
--------------------------------------------------
DBMS_OUTPUT.put_line ('Imprimindo registros a processars');
FOR x IN 1..V_DIAS.count LOOP
DBMS_OUTPUT.put_line ('Dias da semana: '||V_DIAS(x).dias);
--> SE o dia de hoje estiver no dia listado
--> Gerar a cobrança
--> FIM SE
END LOOP;
END;
/
O requisito exige que, se o dia corrente estiver em um dos dias listados, a cobrança para o estabelecimento deve ser gerada. Porém, como saber se o dia de hoje se encaixa na lista de dias válidos?
Existem várias maneiras de fazer isso porém o objetivo aqui é solucionar este problema utilizando-se do recurso de expressões regulares, portanto, vamos a ela:
O primeiro passo é criar uma expressão regular que permita separar os registros. Execute o script abaixo:
set serveroutput on;
DECLARE
v_dias varchar2(10) := '2;4;6';
BEGIN
DBMS_OUTPUT.put_line ('Primeira Posição: '||REGEXP_SUBSTR(v_dias,'[^;]+',1,1));
DBMS_OUTPUT.put_line ('Segunda Posição.: '||REGEXP_SUBSTR(v_dias,'[^;]+',1,2));
DBMS_OUTPUT.put_line ('Terceira Posição: '||REGEXP_SUBSTR(v_dias,'[^;]+',1,3));
END;
/
O segundo passo é criar uma expressão que permita de forma automática qualificar a posição da informação desejada e converter linhas em colunas. Execute o script abaixo várias vezes com diferentes separadores como Ponto e Vírgula(;), ponto (.), vírgula(,) e pipe(|) para ver as consequências:
DECLARE
v_dias varchar2(100) := '&dias_separados_ponto_e_virgula';
BEGIN
FOR x IN (select REGEXP_SUBSTR(str, exp, 1, level) dias_validos
from (select v_dias str
, '[^;]+' exp
from dual)
connect by REGEXP_SUBSTR(str, exp, 1, level) is not null) LOOP
DBMS_OUTPUT.put_line (X.dias_validos);
END LOOP;
END;
/
Desta forma temos a linha convertida em coluna utilizando-se de uma expressão regular a um custo baixo. A partir dai basta adicionar a lógica para geração da cobrança:
set serveroutput on
DECLARE
--------------------------------------------------
--> Array para simular uma tabela
--------------------------------------------------
type r_dias is record (dias varchar2(20));
type t_dias is table of r_dias index by binary_integer;
v_dias t_dias;
--------------------------------------------------
--> Dia corrente
--------------------------------------------------
v_hoje varchar2(1) := TO_CHAR(sysdate,'D');
BEGIN
--------------------------------------------------
--> Alimentando o array
--------------------------------------------------
v_dias(1).dias := '2;4;6'; --> Cenário 1
v_dias(2).dias := '1;3;5'; --> Cenário 2
v_dias(3).dias := '3;5;7'; --> Cenário 3
v_dias(4).dias := '1;2;3;4;5;6;7'; --> Cenário 4
--------------------------------------------------
--> Processando o array
--------------------------------------------------
FOR x IN 1..V_DIAS.count LOOP
-----------------------------------------------
--> Convertendo linhas em colunas
-----------------------------------------------
FOR y IN (select REGEXP_SUBSTR(str, exp, 1, level) dias_validos
from (select V_DIAS(x).dias str
, '[^;]+' exp
from dual)
connect by REGEXP_SUBSTR(str, exp, 1, level) is not null) LOOP
--------------------------------------------
--> Checando o dia
--------------------------------------------
IF Y.dias_validos = v_hoje THEN
DBMS_OUTPUT.put_line ('Para o cenário '||x||' Seria gerada cobrança hoje');
END IF;
END LOOP;
END LOOP;
END;
/
O grande benefício dessa técnica é converter uma linha delimitada por um separador em colunas a um custo baixo, conforme abaixo:
select REGEXP_SUBSTR(str, exp, 1, level) dias_validos
from (select '1;2;3;4;5;6;7' str
, '[^;]+' exp
from dual)
connect by REGEXP_SUBSTR(str, exp, 1, level) is not null
----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 | |* 1 | CONNECT BY WITHOUT FILTERING| | | | | | 2 | FAST DUAL | | 1 | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------- Estatística ---------------------------------------------------------- 1 recursive calls 0 db block gets 0 consistent gets 0 physical reads 0 redo size 406 bytes sent via SQL*Net to client 346 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 7 rows processed