
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

