2011-05-11 14 views
6

Tengo la siguiente consulta que intento usar como un COMANDO en un informe de cristal en el que estoy trabajando.¿Cómo manejar to_date excepciones en una declaración SELECT para ignorar esas filas?

SELECT * FROM myTable 
WHERE to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate} 

Esto funciona bien, sin embargo, mi única preocupación es que la fecha no siempre puede estar en el formato correcto (debido a un error por el usuario). Sé que cuando falla la función to_date arroja una excepción ... ¿es posible manejar esta excepción de forma que ignore la fila correspondiente en mi instrucción SELECT? Porque, de lo contrario, mi informe se rompería si solo se da formato incorrectamente a una fecha en toda la base de datos.

Miré para ver si Oracle ofrece una función isDate, pero parece que se supone que debes manejar la excepción. Cualquier ayuda sería muy apreciada. ¡¡Gracias!!

+2

Bueno, está _supposed_ para almacenar las fechas en las columnas de FECHA. ¿Por qué no? –

+0

Es esta aplicación heredada con la que trabajo en el trabajo. Usan cadenas porque de lo contrario las fechas son predeterminadas a la fecha actual ... No tengo control sobre el DB – ntsue

+0

si tienes fechas en campos varchar2 (malas) y ni siquiera tienes un formato consistente, entonces puedes ser SOL (salvo reconstruir la tabla para convertir todos los formatos de char a un campo FECHA real) – tbone

Respuesta

22

Haciendo eco del comentario de Tony, sería mucho mejor que guarde las fechas en las columnas de FECHA en lugar de forzar una herramienta de consulta para encontrar y manejar estas excepciones.

Si le pegan con un modelo de datos incorrectos, sin embargo, la opción más sencilla es crear una función que hace la conversión y controla el error,

CREATE OR REPLACE FUNCTION my_to_date(p_date_str IN VARCHAR2, 
           p_format_mask IN VARCHAR2) 
    RETURN DATE 
IS 
    l_date DATE; 
BEGIN 
    l_date := to_date(p_date_str, p_format_mask); 
    RETURN l_date; 
EXCEPTION 
    WHEN others THEN 
    RETURN null; 
END my_to_date; 

Su consulta se convertiría entonces en

SELECT * 
    FROM myTable 
WHERE my_to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate} 

Por supuesto, lo más probable es que desee un índice basado en funciones en la llamada MY_TO_DATE para que esta consulta sea razonablemente eficiente.

+0

calificados como mejores prácticas más arreglo para sistemas "heredados" (es decir, rotos) –

+9

+1 Los sistemas heredados son una realidad. Tenemos que tratar con ellos lo mejor que podamos. – APC

4

Si sus datos no son consistentes y las fechas almacenadas como cadenas pueden no ser válidas, entonces tiene 3 opciones.

  1. Refactor su base de datos para asegurarse de que la columna almacena un tipo de datos fecha
  2. controlar la excepción de la cadena hasta la fecha en un procedimiento almacenado
  3. controlar la excepción de la cadena hasta la fecha en un (complejo) de selección de registros fórmula

Sugeriría usar la primera opción ya que sus datos deben ser coherentes.

La segunda opción proporcionará cierta flexibilidad y velocidad ya que el informe solo obtendrá las filas que se necesitan.

La tercera opción forzará al informe a buscar cada registro en la tabla y luego hará que el filtro de informe baje los registros.

+0

Oye, gracias por tu respuesta. Lamentablemente, no tengo control sobre la base de datos ... Sí, estaba pensando en la opción 3 ... pero No quise pagar los gastos generales involucrados. Buscaré primero 2 ... He tenido problemas en el pasado con mi ODBC y los procedimientos almacenados ... pero volveré a verificarlo – ntsue

1

Como dices que "no tienes acceso" a la base de datos, asumo que no puedes crear ninguna función para ayudarte con esto y que solo puedes ejecutar consultas?

Si ese es el caso, entonces el siguiente código debería darle la mayoría de lo que necesita con las siguientes advertencias: 1) El formato de fecha almacenado que desea evaluar es 'mm/dd/aaaa'. Si este no es el caso, puede modificar el código para que se ajuste a su formato. 2) La base de datos no contiene fechas inválidas como el 30 de febrero.

En primer lugar, he creado mi mesa y prueba de datos de prueba:

create table test (x number, sdate varchar2(20)); 
insert into test values (1, null); 
insert into test values (2, '01/01/1999'); 
insert into test values (3, '1999/01/01'); 
insert into test values (4, '01-01-1999'); 
insert into test values (5, '01/01-1999'); 
insert into test values (6, '01-01/1999'); 
insert into test values (7, '12/31/1999'); 
insert into test values (8, '31/12/1999'); 
commit; 

Ahora, la consulta:

WITH dates AS (
    SELECT x 
     , sdate 
     , substr(sdate,1,2) as mm 
     , substr(sdate,4,2) as dd 
     , substr(sdate,7,4) as yyyy 
    FROM test 
    WHERE (substr(sdate,1,2) IS NOT NAN -- make sure the first 2 characters are digits 
      AND to_number(substr(sdate,1,2)) between 1 and 12 -- and are between 0 and 12 
      AND substr(sdate,3,1) = '/' -- make sure the next character is a '/' 
      AND substr(sdate,4,2) IS NOT NAN -- make sure the next 2 are digits 
      AND to_number(substr(sdate,4,2)) between 1 and 31 -- and are between 0 and 31 
      AND substr(sdate,6,1) = '/' -- make sure the next character is a '/' 
      AND substr(sdate,7,4) IS NOT NAN -- make sure the next 4 are digits 
      AND to_number(substr(sdate,7,4)) between 1 and 9999 -- and are between 1 and 9999 
     ) 
) 
SELECT x, sdate 
FROM dates 
WHERE to_date(mm||'/'||dd||'/'||yyyy,'mm/dd/yyyy') <= to_date('08/01/1999','mm/dd/yyyy'); 

Y mis resultados:

X SDATE 
- ---------- 
2 01/01/1999 

La declaración con la voluntad de hacer la mayor parte de la validación para asegurarse de que los valores de fecha estén al menos en el formato adecuado. Tuve que dividir cada unidad de tiempo mes/día/año para realizar la evaluación hasta la fecha porque aún recibía un error de mes no válido cuando hacía una fecha_hora en fecha.

Espero que esto ayude.

2

Tengo el mismo problema ... una antigua base de datos heredada con varios campos para fechas y décadas de datos incorrectos en el campo. Por mucho que me gustaría, tampoco puedo cambiar los tipos de datos. Pero se me ocurrió con esta solución para encontrar si una fecha es actual, que parece ser lo que estás haciendo, así:

select * from MyTable 
where regexp_like(sdate, '[0-1][0-9].[0-3][0-9].[0-9][0-9][0-9][0-9]') 
     -- make sure it's in the right format and ignore rows that are not 
and substr(sdate,7,10) || substr(sdate,1,2) || substr(sdate,4,5) >= to_char({?EndDate}, 'YYYYMMDD') 
     -- put the date in ISO format and do a string compare 

La ventaja de este enfoque es que no se ahogue en fechas como "Febrero 30 ".

0

Confíe en esta respuesta aclara ... no hay EXCEPTION HANDLER directo para la fecha no válida. De una manera fácil, a continuación, una vez que conozca el formato DD/MM/YYYY, la función siguiente REGEXP_LIKE funcionará como un amuleto. to_date() también funcionará, cuando se encuentre la fecha inválida, el cursor irá a OTHERS EXCEPTION. dada a continuación.

DECLARE 
    tmpnum  NUMBER; -- (1=true; 0 = false) 
    ov_errmsg LONG; 
    tmpdate  DATE; 
    lv_date  VARCHAR2 (15); 
BEGIN 
    lv_date := '6/2/2018'; -- this will fail in *regexp_like* itself 
    lv_date := '06/22/2018'; -- this will fail in *to_date* and will be caught in *exception WHEN OTHERS* block 
    lv_date := '07/03/2018'; -- this will succeed 

    BEGIN 
     tmpnum := REGEXP_LIKE (lv_date, '[0-9]{2}/[0-9]{2}/[0-9]{4}'); 

     IF tmpnum = 0 
     THEN            -- (1=true; 0 = false) 
     ov_errmsg := '1. INVALID DATE FORMAT '; 
     DBMS_OUTPUT.PUT_LINE (ov_errmsg); 
     RETURN; 
     END IF; 

     tmpdate := TO_DATE (lv_date, 'DD/MM/RRRR'); 
     --tmpdate := TRUNC (NVL (to_date(lv_date,'DD/MM/RRRR'), SYSDATE)); 

     tmpnum := 1; 
    EXCEPTION 
     WHEN OTHERS 
     THEN 
     BEGIN 
      tmpnum := 0; 
      ov_errmsg := '2. INVALID DATE FORMAT '; 
      DBMS_OUTPUT.PUT_LINE (ov_errmsg || SQLERRM); 
      RETURN; 
     END; 
    -- continue with your other query blocks 
    END; 

    -- continue with your other query blocks 
    DBMS_OUTPUT.PUT_LINE (tmpnum); 
END; 
Cuestiones relacionadas