Problemas para Validar Datas

Dicas do Oracle Forms Builder - Blocos, Itens, LOV, Canvas, Triggers, comandos, PLL, d2kwutil, FMB, Alert, menus, etc
Responder
meguelito
Rank: Programador Sênior
Rank: Programador Sênior
Mensagens: 60
Registrado em: Ter, 17 Jan 2006 1:45 pm
Localização: Santa Catarina
Att.:
Alan Juliano Metzger
Programador Oracle
Inside System Informática
Msn/E-mail: alanjuliano@yahoo.com.br

Boa Tarde galera,

Poxa eu sempre me bato quando preciso fazer validação entre datas do tipo que uma data não pode sobrepor a outra, ai não sei direito onde posso fazer a validação, eu sempre faço no WHEN-VALIDATE-ITEM e na PRE-INSERT, mas sempre tem furo, resumindo como eu posso fazer uma validação num bloco multi-record onde as datas não podem se sobrepor.

Ex.: digito 01/01/2008 até 15/01/2008 ai se eu digitar 31/12/2007 e tentar entrar com a data 06/01/2008 ele não deve deixar.

Alguém pode dar essa força que eu acho que é do interesse de todos.
Trevisolli
Moderador
Moderador
Mensagens: 2016
Registrado em: Qua, 12 Jan 2005 3:25 pm
Localização: Araraquara - SP
Abraço,

Trevisolli
OCA Oracle PL/SQL Developer Certified Associate
OCP Oracle Forms Developer Certified Professional
Araraquara-SP

Brother,

O que a gente costuma fazer, é uma função (pode ser até de banco), onde você passa varrendo as linhas (PK's) e, verifica o período que está digitando, se está em algum intervalo de linhas já existentes.

É isso que deseja fazer?
Caso positivo, vejo se tenho um exemplo aqui e te envio.
Avatar do usuário
Porva
Rank: DBA Sênior
Rank: DBA Sênior
Mensagens: 342
Registrado em: Seg, 29 Jan 2007 7:36 am
Localização: São Paulo/SP
Rafael S. Nunes
São Paulo/SP

véio, isso é o 'pulo do gato', vê se você consegue entender:

Ex.: um intervalo cadastrado não pode ser sobreposto por outras datas que compreendam esse mesmo intervalo.

Selecionar tudo

SELECT count(*) 
  INTO vn_count 
  FROM rhpessarea
 WHERE rhpessarea.cent_cd = :b01.cent_cd
   AND rhpessarea.area_cd = :b01.area_cd 
   AND rhpessarea.pess_cd = :b01.pess_cd		 			 

   ------------------ ESSE É A PARTE QUE INTERESSA ------------------ 
   AND rhpessarea.pesa_dt_inicial <= NVL(:b01.pesa_dt_final, rhpessarea.pesa_dt_inicial)	 
   AND NVL(rhpessarea.pesa_dt_final, :b01.pesa_dt_inicial) >= :b01.pesa_dt_inicial	  
 
   AND rhpessarea.rowid != NVL(:b01.rowid, 0); --talvez isso não seja necessário, se alguém puder esclarecer melhor
obs: pra ficar fácil pra você adapatar pro seu caso aí, atenta para os nomes 'dt_inicial' e 'dt_final' somente, pra não embolar o entendimento devido aos outros campos.
Avatar do usuário
Porva
Rank: DBA Sênior
Rank: DBA Sênior
Mensagens: 342
Registrado em: Seg, 29 Jan 2007 7:36 am
Localização: São Paulo/SP
Rafael S. Nunes
São Paulo/SP

ou seja, ele vai retornar o Count caso o período que você está informando NÃO ESTEJA CADASTRADO, ou seja, esteja disponível.
Trevisolli
Moderador
Moderador
Mensagens: 2016
Registrado em: Qua, 12 Jan 2005 3:25 pm
Localização: Araraquara - SP
Abraço,

Trevisolli
OCA Oracle PL/SQL Developer Certified Associate
OCP Oracle Forms Developer Certified Professional
Araraquara-SP

Brother,

Eu tinha este teste aqui na minha máquina.
No caso, eu passo apenas uma data, e ele valida pra mim, numa determinada tabela, se pode ou não ser utilizada esta data.

É só adaptar, caso queira passar duas datas para validação.

Selecionar tudo

CREATE TABLE tb_teste (cod NUMBER, dt_ini DATE, dt_fim date);

INSERT INTO tb_teste VALUES (1,TO_DATE('01-jan-2007'),TO_DATE('15-jan-2007'));
INSERT INTO tb_teste VALUES (2,TO_DATE('01-jun-2007'),TO_DATE('30-jun-2007'));
INSERT INTO tb_teste VALUES (3,TO_DATE('02-jul-2007'),TO_DATE('31-dec-2007'));

COMMIT;

DECLARE
   
   v_valida BOOLEAN;

   FUNCTION fun_data_valida (p_data DATE)
   RETURN BOOLEAN 
   IS
     CURSOR cur_param
       IS 
         SELECT dt_ini,
                dt_fim
           FROM tb_teste
       ORDER BY cod; 
   BEGIN 
      FOR x IN cur_param
      LOOP
        
         IF p_data BETWEEN x.dt_ini AND x.dt_fim
         THEN 
           RETURN FALSE;
         END IF;
         
      END LOOP;            
   
      RETURN TRUE;
         
   END fun_data_valida; 

   
BEGIN
  v_valida := fun_data_valida(TO_DATE('31-DEC-2006'));
  
  IF v_valida
  THEN
     dbms_output.put_line('Data Válida. Este período pode ser utilizado! ');
  ELSE 
     dbms_output.put_line('Data incorreta. Período já utilizado. ');   
  END IF;   
END;

Esse código acima, foi desenvolvido num bloco anônimo, mas, poderia ser uma função do próprio forms ou, de banco mesmo.

Se ajudar, está valendo.
qualquer coisa, manda ai.
rogenaro
Rank: DBA Pleno
Rank: DBA Pleno
Mensagens: 232
Registrado em: Sex, 30 Mar 2007 7:26 pm
Localização: Londrina - PR
Rafael O. Genaro

Aproveitando a tabela do exemplo do trevisolli, segue a lógica que uso para validar se um período já foi cadastrado (Este seria o modo mais simples, onde as validações reais são realizadas apenas no PRE-INSERET e PRE-UPDATE, já que os dados que se encontram apenas na tela não são comparados entre si durante antes de um post ou commit do form).

* Obs: considerando que nenhuma das datas pode ser nula, e que já foi previamente validado que a data final >= data inicial.

Geralmente coloco este tipo de validação em uma Program Unit, quee chamo tanto no WHEN-VALIDATE-RECORD quando nas triggers PRE-INSERT e PRE-UPDATE.

Selecionar tudo

select count(1) existe
from   dual
where  exists
      (
       select 1 from tb_teste t
       where  (  t.dt_ini between to_date(:bloco.dt_ini, 'yyyymmdd') and to_date(:bloco.dt_fim, 'yyyymmdd')
              or t.dt_fim between to_date(:bloco.dt_ini, 'yyyymmdd') and to_date(:bloco.dt_fim, 'yyyymmdd')
              or to_date(:bloco.dt_ini, 'yyyymmdd') between t.dt_ini and t.dt_fim
              or to_date(:bloco.dt_fim, 'yyyymmdd') between t.dt_ini and t.dt_fim
              )
       and    (t.rowid != :bloco.rowid or :bloco.rowid is null) -- Para evitar a comparação com o próprio registro em um update...
      );
Se existe = 1, o período já foi cadastrado.
Caso contrário, existe = 0, e o período ainda não foi cadastrado.

Exemplos:

* O novo período commpreende todos os demais registros cadastrados:

Selecionar tudo

   dt_ini = 20010101 e dt_fim = 20091231
* O novo período inicia em um período cadastrado em um registro, e termina em um período cadastrado em outro registro:

Selecionar tudo

   dt_ini = 20070114 e dt_fim = 20070714
* O novo período inicia dentro do período já cadastrado, e termina em uma data posterior a todos os demais períodos cadastrados:

Selecionar tudo

   dt_ini = 20070801 e dt_fim = 29991231
* O novo período inicia em uma data vaga, e termina em uma data vaga, porém existe um registro já cadastrado que se encontra dentro deste período:

Selecionar tudo

   dt_ini = 20070201 e dt_fim = 20070701
* O novo período tem uma data inicial a menor que as demais datas cadastradas, porém se encerra com uma data posterior à data inicial de algum registro:

Selecionar tudo

  dt_ini = 20010101 e dt_fim = 20070701
Avatar do usuário
Porva
Rank: DBA Sênior
Rank: DBA Sênior
Mensagens: 342
Registrado em: Seg, 29 Jan 2007 7:36 am
Localização: São Paulo/SP
Rafael S. Nunes
São Paulo/SP

só fazendo uma correção no exemplo que passei:

'ou seja, ele vai retornar o Count (maior que 0) caso o período que você está informando NÃO ESTEJA CADASTRADO, ou seja, esteja disponível.'

na verdade, é o contrário, se retornar o Count > 0 é porquê o perído JÁ ESTÁ cadastrado.
acarsil
Rank: Estagiário Pleno
Rank: Estagiário Pleno
Mensagens: 4
Registrado em: Qua, 29 Jun 2005 11:54 am
Localização: SP

Aproveitando este tópico. Como devo proceder caso queira validar somente as datas que estão em tela, ou seja, ainda não foram comitadas em banco?
Segue exemplo de rotina que estou tentando montar. Funciona em parte mas ainda está com fuos na validação. Se alguém puder ajudar, fico agradecido.

Selecionar tudo

PROCEDURE VALIDA_DT_TERMINO_AGRUP IS
	v_dt_inicio		date;
	v_dt_termino		date;
	v_teste			number;
	v_existe		varchar2(1);
BEGIN	

	v_dt_inicio	:=	:agrcob_vig.dt_inicio_vigencia;
	v_dt_termino	:=	:agrcob_vig.dt_final_vigencia;
	
	FIRST_RECORD;
	LOOP
		v_existe	:=	'N';
		v_teste 	:= 	:system.cursor_record;				
		first_record;
		loop
			if	(v_dt_inicio	between	:agrcob_vig.dt_inicio_vigencia	and	:agrcob_vig.dt_final_vigencia	)then --	or
				--(:agrcob_vig.dt_inicio_vigencia >= v_dt_inicio) 	then
				v_existe := 'S';
			elsif 	v_dt_termino	between	:agrcob_vig.dt_inicio_vigencia	and	:agrcob_vig.dt_final_vigencia	then
				v_existe := 'S';
			end if;
			if	v_teste	= 1 then
				v_existe := 'N';
			end if;
			if	:system.cursor_record = v_teste	then
				v_existe := 'N';
			end	if;
			if v_existe ='S' then
				mensagem ('Existem datas entre os intervalos.');
				raise	form_trigger_failure;	
			end if;
			if	:system.cursor_record = v_teste	then
				exit;
			end	if;
			next_record;
		end loop;
		go_record(v_teste);
		if	:system.last_record	=	'TRUE'	then
			exit;
		end	if;
		next_record;
	END 	loop;
EXCEPTION
	when	form_trigger_failure then
	raise form_trigger_failure;
	WHEN	OTHERS THEN
	CGTE$OTHER_EXCEPTIONS;
END;
Trevisolli
Moderador
Moderador
Mensagens: 2016
Registrado em: Qua, 12 Jan 2005 3:25 pm
Localização: Araraquara - SP
Abraço,

Trevisolli
OCA Oracle PL/SQL Developer Certified Associate
OCP Oracle Forms Developer Certified Professional
Araraquara-SP

Brother,

Pra verificação das datas digitadas ainda na tela, que não estão no banco, você pode "simular" essa informação no banco, com o comando: Só analise o local ideal pra se colocar essa informação/comando (Talvez no teu When-Validate-Record).
Responder
  • Informação
  • Quem está online

    Usuários navegando neste fórum: Nenhum usuário registrado e 17 visitantes