2011-01-25 12 views
7

Tengo la siguiente tabla y estoy intentando detectar productos que tienen una interrupción en sus tramos.Oracle SQL: detección de interrupciones en tramos continuos

Product  | unit_Cost | price start date | price end date 
-------------------------------------------------------------------------- 
product 1  15.00   01/01/2011  03/31/2011 
product 1  15.00   04/01/2011  06/31/2011 
product 1  15.00   07/01/2011  09/31/2011 
product 1  15.00   10/01/2011  12/31/2011 

product 2  10.00   01/01/2011  12/31/2011 

product 3  25.00   01/01/2011  06/31/2011 
product 3  25.00   10/01/2011  12/31/2011 

Así que aquí lo quiero que informe product3 porque nos falta el lapso

07/01/2011 hasta 09/31/2011

¿Alguna idea sobre cómo puedo hacer esto?

EDIT: Oracle Ver: 10g

Create Table Statement 

CREATE TABLE Sandbox.TBL_PRODUCT 
(
    PRODUCT_ID  VARCHAR2(13 BYTE), 
    PRODUCT   VARCHAR2(64 BYTE), 
    UNIT_COST   NUMBER, 
    PRICE_START_DATE DATE, 
    PRICE_END_DATE DATE 
) 

EDITAR 2 fechas de inicio y final no se pueden superponer

EDITAR 3 un lapso puede ser cualquiera de las dos fechas, siempre y cuando price_end_date> = price_start_date. Igual está incluido ya que un producto puede estar a la venta por un día.

+1

Necesitamos su consulta, y potencialmente la declaración 'CREATE TABLE' para la (s) tabla (s) involucradas, para poder ayudarlo. Y la versión de Oracle ... ¿Por qué la gente está votando una pregunta incompleta? –

+0

¿Pueden las fechas de inicio y final se superponen? – DCookie

+0

¿Siempre le preocupa un solo año? Y supongamos que tengo los primeros 6 meses de un año, pero nada hasta el final del año, ¿es posible? Por ejemplo, supongamos que el producto 3 tiene solo el primer registro. ¿Debería ser reportado? – DCookie

Respuesta

2

Prueba este (usando LEAD función analítica):

SELECT * 
    FROM (
       SELECT a.*, LEAD(price_start_date,1,NULL) OVER(PARTITION BY product ORDER BY price_end_date) next_start_date 
     FROM Product a 
     ) 
WHERE (price_end_date + 1)<> next_start_date 

Ejemplo con la instalación

 CREATE TABLE PRODUCT 
      (
      PRODUCT VARCHAR2(100 BYTE), 
      UNIT_COST NUMBER, 
      START_DATE DATE, 
      END_DATE DATE 
     ); 

     INSERT INTO Product VALUES('product 1','15.00',TO_DATE('01/01/2011','MM/DD/RRRR'),TO_DATE('03/31/2011','MM/DD/RRRR')); 
     INSERT INTO Product VALUES('product 1','15.00',TO_DATE('04/01/2011','MM/DD/RRRR'),TO_DATE('06/30/2011','MM/DD/RRRR')); 
     INSERT INTO Product VALUES('product 1','15.00',TO_DATE('07/01/2011','MM/DD/RRRR'),TO_DATE('09/30/2011','MM/DD/RRRR')); 
     INSERT INTO Product VALUES('product 1','15.00',TO_DATE('10/01/2011','MM/DD/RRRR'),TO_DATE('12/31/2011','MM/DD/RRRR')); 
     INSERT INTO Product VALUES('product 2','10.00',TO_DATE('01/01/2011','MM/DD/RRRR'),TO_DATE('12/31/2011','MM/DD/RRRR')); 
     INSERT INTO Product VALUES('product 3','25.00',TO_DATE('01/01/2011','MM/DD/RRRR'),TO_DATE('06/30/2011','MM/DD/RRRR')); 
     INSERT INTO Product VALUES('product 3','25.00',TO_DATE('10/01/2011','MM/DD/RRRR'),TO_DATE('12/31/2011','MM/DD/RRRR')); 

SELECT * 
    FROM (
       SELECT a.*, LEAD(start_date,1,NULL) OVER(PARTITION BY product ORDER BY start_date) next_start_date 
       FROM Product a 
      ) 
WHERE (end_date + 1)<> next_start_date 

EDITAR: Actualicé la consulta para considerar el siguiente start_date y current end_date para evitar problemas con la distribución de los datos.

+0

¿Por qué agrega 3 meses a la fecha de inicio? ¿Estás asumiendo que los datos están organizados trimestralmente? No estoy seguro de que esa suposición esté justificada. Supongamos que el producto 3 tiene un registro más para la brecha faltante. Su consulta todavía informa que falta. Creo que su enfoque es bueno, solo necesita ajustes. Creo que deberías agregar un día a cada end_date y verificarlo con tu next_start_date. – DCookie

+0

@DCookie: Sí, supongo que los datos están organizados de manera encubierta. Como OP está hablando de tramos, debe haber un período predefinido que se debe usar. Si OP puede proporcionar más información sobre el tramo, la consulta se puede ajustar fácilmente para acomodar los tramos. – Chandu

+0

Excepto que usted sabe que los datos no están organizados de esa manera, ya que el producto 2 tiene un registro para todo el año, y el producto 3 tiene un registro para la mitad del año. – DCookie

0

Puede usar una cláusula exists para filtrar las filas para las que existe una fila anterior, y una cláusula not exist para buscar filas donde la fila anterior no termina en la fila actual más un día. Por ejemplo:

select * 
from TBL_PRODUCT t1 
where exists 
     (
     select * 
     from TBL_PRODUCT t2 
     where t2.PRODUCT = t1.PRODUCT 
       and t2.PRICE_END_DATE < t1.PRICE_START_DATE 
     ) 
     and not exists 
     (
     select * 
     from TBL_PRODUCT t3 
     where t3.PRODUCT = t1.PRODUCT 
       and t3.PRICE_END_DATE + 1 = t1.PRICE_START_DATE 
     ); 

Este grabados:

PRODUCT   UNIT_COST PRICE_STA PRICE_END 
----------------------- ---------- --------- --------- 
product 3   25 01-OCT-11 31-DEC-11 
0

Usted puede hacer una comparación matemática de los rangos, suponiendo que arreglar las malas fechas en su conjunto de la muestra:

SELECT PRODUCT 
FROM Sandbox.TBL_PRODUCT 
HAVING SUM(PRICE_END_DATE - PRICE_START_DATE + 1) < MAX(PRICE_END_DATE) - MIN(PRICE_START_DATE) + 1 
GROUP BY PRODUCT 

cual se devolvía:

PRODUCT                   
----------------- 
product 3                  
1 row selected 
1

También es posible usar esta técnica. Utiliza una consulta interna (chronological_record) para asignar un rango a cada registro en la tabla TBL_PRODUCT (el rango se ordena en start_date dentro de cada product).

WITH 
    chronological_record AS 
    (
    SELECT 
     product, 
     unit_cost, 
     start_date, 
     end_date, 
     (DENSE_RANK() OVER (PARTITION BY product ORDER BY start_date)) 
      AS chronological_order 
    FROM 
     TBL_PRODUCT 
) 

SELECT 
    earlier.product, 
    (earlier.end_date + 1) AS missing_period_start_date, 
    (later.start_date - 1) as missing_period_end_date 
FROM 
    CHRONOLOGICAL_RECORD earlier 
    INNER JOIN 
    CHRONOLOGICAL_RECORD later 
    ON 
     earlier.product = later.product 
     AND 
     (earlier.chronological_order + 1) = later.chronological_order 
WHERE 
    (earlier.end_date + 1) <> later.start_date 

En su ejemplo, la subconsulta (chronological_record) daría algo como esto:

 
Product | unit_Cost | start date | end date | chronological_order 
-------------------------------------------------------------------------- 
product 1 15.00  01/01/2011 03/31/2011 1 
product 1 15.00  04/01/2011 06/31/2011 2 
product 1 15.00  07/01/2011 09/31/2011 3 
product 1 15.00  10/01/2011 12/31/2011 4 

product 2 10.00  01/01/2011 12/31/2011 1 

product 3 25.00  01/01/2011 06/31/2011 1 
product 3 25.00  10/01/2011 12/31/2011 2 

de INNER JOIN partidos efectivamente anterior registra con sus registros (cronológicamente hablando) El siguiente consulta principal.

+0

ciertamente funciona, aunque parece un poco largo en comparación con el uso de 'plomo (...)' o 'lag (...)'? – araqnid

+0

@araqnid: Para ser sincero, no sabía acerca de 'LEAD' o' LAG' antes de hoy. ¡Gracias por el consejo! –

1

Asumiendo que su tabla se denomina products, su columna de fecha de inicio es el nombre s y su columna de fecha de finalización es el nombre e:

create view max_interval as 
select product, 
max(e) - min(s) as max_interval 
from products group by product; 


create view total_days as 
select product, 
sum(e - s) + count(product) - 1 as total_days 
from products group by product ; 

Entonces esta consulta le da todos los productos con "perdidos" vanos:

select a.*, b.* 
from max_interval a 
left outer join total_days b 
on (a.product = b.product) 
where a.max_interval <> b.total_days; 

Dado que el grupo por es el mismo en ambas vistas, esto, por supuesto, se puede combinar en una sola consulta, aunque la solución sea un poco menos clara:

select product, 
max(e) - min(s) as max_interval, 
sum(e - s) + count(product) - 1 as total_days 
from products group by product 
having max(e) - min(s) <> sum(e - s) + count(product) - 1; 

Pero, como señala Stephanie Page, esa es una optimización prematura; es poco probable que escanee por descansos en tramos continuos con tanta frecuencia.

+2

Enfoque interesante, aunque significa escanear la tabla de productos dos veces. Sin embargo, ¿no se pueden combinar en una sola consulta? – araqnid

+2

¿Has visto muchas tablas de productos con miles de millones de filas? ¿Cuál es el problema con ** DOS ** escaneos de una tabla que probablemente esté completamente en la memoria caché de todos modos. ¿Con qué frecuencia se ejecutará un análisis de "Brecha"? ¿Con qué frecuencia está actualizando las tablas de "productos"? –