code review sql – Filtrar registros de fechas que no se encuentran dentro o abarcan un rango de fechas

Pregunta:

Tengo registros que tienen períodos de tiempo específicos. La necesidad es seleccionar registros que se adhieran a las siguientes tres reglas (léalas como or condiciones) con respecto a un período de tiempo objetivo:

  1. La fecha de inicio del registro puede comenzar dentro del período de tiempo objetivo y la fecha de finalización no importa.
  2. La fecha de finalización del registro coincide con el período de tiempo objetivo y la fecha de inicio es intrascendente.
  3. Las fechas del registro en realidad abarcan el tiempo objetivo.

Entonces, básicamente, una consulta debe devolver todos los registros que comienzan, finalizan o abarcan un período de tiempo objetivo.

¿Existe una forma más eficiente en Oracle 11g de hacer las reglas anteriores que lo que he establecido actualmente en el SQL a continuación?


He creado una prueba SQL Fiddle Test para el siguiente esquema y SQL

Esquema

CREATE TABLE dates_test (
  id         NUMBER,
  hint       VarChar(20),
  BEGIN_DATE DATE,
  END_DATE DATE
);
-- If the span is Mar 6 to April 6 then 2, 3 and 4 are valid -
INSERT INTO dates_test VALUES (1, 'exclude before', TO_DATE('01-JAN-2014','DD-MON-YYYY'), TO_DATE('05-JAN-2014','DD-MON-YYYY'));
INSERT INTO dates_test VALUES (2, 'spans',          TO_DATE('05-MAR-2014','DD-MON-YYYY'), TO_DATE('08-SEP-2014','DD-MON-YYYY'));
INSERT INTO dates_test VALUES (3, 'Starts within',  TO_DATE('07-MAR-2014','DD-MON-YYYY'), TO_DATE('07-SEP-2014','DD-MON-YYYY'));
INSERT INTO dates_test VALUES (4, 'Ends within',    TO_DATE('01-JAN-2014','DD-MON-YYYY'), TO_DATE('07-MAR-2014','DD-MON-YYYY'));
INSERT INTO dates_test VALUES (5, 'exclude after',  TO_DATE('06-JUN-2014','DD-MON-YYYY'), TO_DATE('07-SEP-2014','DD-MON-YYYY'));
INSERT INTO dates_test VALUES (6, 'within OK',  TO_DATE('08-MAR-2014','DD-MON-YYYY'), TO_DATE('09-MAR-2014','DD-MON-YYYY'));

Aquí está el SQL que devuelve lo que necesito, pero ¿se puede mejorar?

select * from
dates_test t0
where 
(  -- Starts Within range
  ( t0.BEGIN_DATE 
    BETWEEN TO_DATE('03-06-2014', 'mm-dd-yyyy') 
    AND     TO_DATE('04-06-2014', 'mm-dd-yyyy')
  )
OR  -- Ends within range
  (
    t0.END_DATE
    BETWEEN TO_DATE('03-06-2014', 'mm-dd-yyyy') 
    AND     TO_DATE('04-06-2014', 'mm-dd-yyyy')    
   )
OR    -- SPANS Range
   (
     t0.BEGIN_DATE < TO_DATE('03-06-2014', 'mm-dd-yyyy')
     AND
     t0.END_DATE > TO_DATE('04-06-2014', 'mm-dd-yyyy')   
    )
);

Resultado:

| ID |          HINT |                     BEGIN_DATE |                         END_DATE |
|----|---------------|--------------------------------|----------------------------------|
|  2 |         spans |   March, 05 2014 00:00:00+0000 | September, 08 2014 00:00:00+0000 |
|  3 | Starts within |   March, 07 2014 00:00:00+0000 | September, 07 2014 00:00:00+0000 |
|  4 |   Ends within | January, 01 2014 00:00:00+0000 |     March, 07 2014 00:00:00+0000 |
|  5 |     within OK |   March, 08 2014 00:00:00+0000 |     March, 09 2014 00:00:00+0000 |

Respuesta:

Entonces, mi primera respuesta salió mal (¿puedo culpar a los datos de prueba incompletos?)

Es bueno tener en cuenta que debe diseñar sus pruebas para cubrir las situaciones que son importantes para usted. Debido a que sus pruebas estaban incompletas, cuando ejecuté y modifiqué su código y obtuve los 'mismos' resultados, asumí que el código era bueno. Nunca validé tus casos de prueba (culpa mía).

Esta reelaboración me hizo darme cuenta de que sus datos se servirían mejor con un caso de prueba negativo. Lo que desea probar son rangos de exclusión. Si puede asumir que sus datos son válidos (lo es, correcto), entonces solo desea probar aquellos casos en los que no hay intersección.

No hay intersección si la fecha de la prueba finaliza antes de la fecha de los datos o si la fecha de los datos finaliza antes de la fecha de la prueba. Esto simplifica la consulta a:

select *
from dates_test t0
where not (
      END_DATE < TO_DATE('03-06-2014', 'mm-dd-yyyy')
   or BEGIN_DATE > TO_DATE('04-06-2014', 'mm-dd-yyyy')

Como se me señaló (muy bien, debo agregar), la lógica anterior se puede simplificar aún más usando la lógica booleana 'básica' para:

select *
from dates_test t0
where END_DATE >= TO_DATE('03-06-2014', 'mm-dd-yyyy')
  and BEGIN_DATE <= TO_DATE('04-06-2014', 'mm-dd-yyyy'))

cual es mi respuesta final (gracias @mjolka)

Usando su violín SQL (revisado), la consulta anterior produce (tenga en cuenta que sus datos revisados ​​tienen dos registros con id 5):

| ID |          HINT |                     BEGIN_DATE |                         END_DATE |
|----|---------------|--------------------------------|----------------------------------|
|  2 |         spans |   March, 05 2014 00:00:00+0000 | September, 08 2014 00:00:00+0000 |
|  3 | Starts within |   March, 07 2014 00:00:00+0000 | September, 07 2014 00:00:00+0000 |
|  4 |   Ends within | January, 01 2014 00:00:00+0000 |     March, 07 2014 00:00:00+0000 |
|  5 |     within OK |   March, 08 2014 00:00:00+0000 |     March, 09 2014 00:00:00+0000 |

Leave a Comment

Your email address will not be published.

Scroll to Top

istanbul avukat

-

web tasarım