2012-09-24 29 views
5

Tengo algunos datos en una tabla que se parece a esto:consulta iterativa sin utilizar CTE

 
Item Date   Price 
A  8/29/2012 $3 
B  8/29/2012 $23 
C  8/29/2012 $10 
A  8/30/2012 $4 
B  8/30/2012 $25 
C  8/30/2012 $11 
A  8/31/2012 $3 
B  8/31/2012 $22 
C  8/31/2012 $8 
A  9/01/2012 $3 
B  9/01/2012 $26 
C  9/01/2012 $9 
A  9/02/2012 $3 
B  9/02/2012 $24 
C  9/02/2012 $9 

Tengo que escribir una consulta que identifica a ese precio por A no ha cambiado desde el 30/08/2012, el precio del artículo C no ha cambiado desde el 9/01/2012, y para devolver el número de días transcurridos para ambos (estamos buscando los artículos con precios sin movimiento). No puedo usar CTE o cursores, o tablas temporales de creadas por separado (seleccionar en, etc.) debido a las limitaciones de la herramienta de informe web en la que este sql necesita ejecutarse. Solo puedo usar una selección de paso único 'básico' consulta (aunque las subconsultas funcionarán). ¿Alguien tiene alguna idea astuta sobre cómo lograr esto?

Mi primer intento fue agrupar por artículo y el precio, donde el precio es el mismo que el último precio , having count > 2, identificando la fecha min y haciendo un datediff entre la fecha min y getdate. Sin embargo, esto solo identifica la primera instancia de ese precio, no tiene en cuenta ninguna fila subsiguiente que pueda tener un precio diferente de . Espero que tenga sentido.

+0

que DBMS estás usando? PostgreSQL? ¿Oráculo? DB2? –

+1

Por cierto: siempre puede escribir una vista que devuelva la información necesaria y luego hacer un SELECTO desde esa vista desde su herramienta de informes –

+0

Lo siento, debería haber dicho, este es SQL Server 2008. Lamentablemente, tampoco puedo escribir una vista - ¡Esta es una base de datos alojada por un proveedor y tenemos derechos de solo lectura para ella! – Luke

Respuesta

2

me he dado ambos tipos de edades y cuenta para los elementos que existen sólo una vez en la mesa (que no tienen una fecha antigua). Por favor, háganos saber qué tan cerca está esto.

ACTUALIZACIÓN: tuve que corregir el cálculo de la fecha en "PriceAgeToNow", y también he intentado filtrar los registros que solo han tenido un nuevo precio por 1 día. Aquí está el SQL Fiddle.

-- Get the age of the current price 
select * 
    , datediff(d, c.OldDate, getdate()) as PriceAgeToNow 
    , datediff(d, c.OldDate, c.NewDate) as PriceAgeToNewestDate 
from (
    select * 
     -- Get max date of former price 
     , isnull(
      (select max(Date) from PricingTable where Item = b.Item and Date < b.NewDate and Price != b.NewPrice), b.NewDate 
     ) as OldDate 
    from (
     -- Get current price 
     select * 
      , (select Price from PricingTable where Item = a.Item and Date = a.NewDate) as NewPrice 
     from (
      -- Get current date 
      select Item 
       , max(Date) as NewDate 
      from PricingTable 
      group by Item 
     ) a 
    ) b 
) c 
-- Attempt to filter out price changes that have only lasted 1 day 
where datediff(d, c.OldDate, c.NewDate) > 1 
+0

Funciona, pero incluye las filas 'B' (que el OP excluye porque cambiaron 'ayer'). –

+0

Buena llamada, todavía no he visto todos los comentarios. Debería ser bastante fácil filtrar dichos registros, pero estoy teniendo problemas para entender la definición exacta para ellos en los comentarios. –

+0

Eso se ve muy prometedor Tim, ¡muchas gracias! Necesitaré un ratito para jugar con él y adaptarlo a los datos reales; te contaré cómo funciona. De nuevo, muy apreciado. – Luke

2

* Actualización with fiddle: *

¿Funcionará?

SELECT ct.Item, 
    ct.Price, 
    datediff(day, max(yt.[Date]), ct.[Date]) AS ChangeDays 
FROM 
    (SELECT Item, max(Date) as LastDate FROM YourTable GROUP BY Item) maxdata 
    INNER JOIN YourTable ct on ct.Item = maxdata.Item and ct.[Date] = maxdata.LastDate 
    INNER JOIN YourTable yt on yt.Item = ct.Item and yt.[Date] < ct.[Date] and yt.Price <> ct.Price 
GROUP BY ct.Item, ct.Price, ct.[Date] 
+0

¡Gracias Jason! Jugando con esto ahora también. Apreciamos mucho la ayuda. – Luke

2

Tengo una preferencia para usar las funciones de Windows donde son más apropiadas. Y, en este caso, hay un truco interesante para encontrar secuencias de cosas que son las mismas. Enumérelos (con row_number) sobre todos los datos, particionados por artículo y ordenados por fecha. Y luego enumerarlos, sobre todos los datos, divididos por artículo y precio y ordenados por fecha.

para A, se obtendría

A 8/29 $3 1 1 
A 8/30 $4 2 1 
A 8/31 $3 3 2 
A 9/1 $3 4 3 
A 9/3 $3 5 4 

La diferencia entre las dos últimas columnas es constante, para cualquier secuencia de precios que se mantienen constantes en los datos. Entonces podemos usar esto para encontrar cuándo comienza esa secuencia. La siguiente consulta toma este enfoque:

select item, price, (id_seqnum - ipd_seqnum) as groupid, min(date) as mindate 
from (select p.*, 
      row_number() over (partition by item order by date) id_seqnum, 
      row_number() over (partition by item, price order by date) as ipd_seqnum, 
      max(date) over (partition by item) as maxdate 
     from prices p 
    ) t 
group by item, price, (id_seqnum - ipd_seqnum) 
having max(date) = max(maxdate) 

También encontrar la fecha máxima para cada elemento y, a continuación, elige la agrupación que tiene la fecha máxima.

+0

Gracias Gordon. Esa es una alternativa interesante: jugaré con eso también. Aprendiendo mucho hoy :-) – Luke