2012-09-24 14 views
5

Ich habe einige Daten in einer Tabelle, die in etwa so aussieht:Iterative Abfrage ohne CTE mit

 
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 

Ich brauche eine Abfrage zu schreiben, die für einen Preis identifiziert hat nicht geändert Seit dem 30.08.2012 hat sich der Preis für Artikel C seit dem 01.09.2012 nicht geändert, und gibt die Anzahl der Tage zurück, die für beide Tage verstrichen sind (wir suchen Artikel mit nicht beweglichen Preisen). Ich kann CTE oder Cursor oder separat erstellte temporäre Tabellen (wählen Sie in, etc) wegen der Einschränkungen des Web-Bericht Werkzeug dieses sql muss nicht ausführen. Ich kann nur eine "einfache" Single-Pass-Select verwenden Abfrage (Unterabfragen werden jedoch funktionieren). Hat jemand irgendwelche schlauen Ideen, wie man dies erreicht? ?

Mein erster Versuch war zu einer Gruppe von Artikeln und Preis, wo der Preis ist die gleiche wie der neueste Preis, having count > 2, den min Zeitpunkt zu identifizieren und eine datediff zwischen min Datum und getdate tun. Allerdings identifiziert dies nur die erste Instanz von dieser Preis, es berücksichtigt keine nachfolgenden Zeilen, die möglicherweise einen anderen Preis haben. Hoffnung, die Sinn macht.

+0

welche DBMS verwenden Sie? PostgreSQL? Orakel? DB2? –

+1

Btw: Sie können immer eine Ansicht schreiben, die die benötigten Informationen zurückgibt und dann eine SELECT aus dieser Sicht aus Ihrem Reporting-Tool –

+0

Sorry, sollte gesagt haben, das ist SQL Server 2008. Leider kann ich auch keine Ansicht schreiben - Dies ist eine vom Anbieter gehostete Datenbank und wir haben nur Leseberechtigungen dafür! – Luke

Antwort

2

Ich habe beide Arten von Alter und Konto für Elemente, die nur einmal in der Tabelle vorhanden sind (sie haben kein altes Datum). Bitte lassen Sie uns wissen, wie nah das ist.

UPDATE: Ich musste die Datumsberechnung in "PriceAgeToNow" korrigieren, und ich habe auch versucht, Datensätze auszufiltern, die nur einen neuen Preis für 1 Tag haben. Hier ist die 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

Funktioniert, enthält jedoch 'B'-Zeilen (die das OP ausschließt, weil sie 'gestern' geändert haben). –

+0

Guter Anruf, ich habe noch nicht alle Kommentare gesehen. Es sollte einfach genug sein, solche Datensätze herauszufiltern, aber ich habe Probleme, die genaue Definition für sie in den Kommentaren zu verstehen. –

+0

Das sieht sehr vielversprechend aus Tim, vielen Dank! Ich brauche eine Weile, um mit den tatsächlichen Daten zu spielen - damit du weißt, wie es läuft. Nochmals sehr geschätzt. – Luke

2

* Aktualisierte with fiddle: *

Wird diese Arbeit?

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

Danke Jason! Spiele jetzt auch damit. Ich schätze die Hilfe sehr. – Luke

2

Ich habe eine Vorliebe für die Verwendung von Windows-Funktionen, wo sie am besten geeignet sind. Und in diesem Fall gibt es einen interessanten Trick, um Sequenzen von Dingen zu finden, die gleich sind. Zählen Sie sie (mit row_number) über alle Daten auf, partitioniert nach Element und geordnet nach Datum. Und dann zähle sie auf, über alle Daten, unterteilt nach Artikel und Preis und sortiert nach Datum.

Für A, würden Sie

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 

Der Unterschied zwischen den letzten beiden Spalten konstant ist, für jede Folge von Preisen erhalten, die in den Daten konstant bleiben. Wir können dies dann verwenden, um zu finden, wann diese Sequenz beginnt. Die folgende Abfrage nimmt diesen Ansatz:

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) 

Es ist auch das maximale Datum für jedes Element finden, und wählt dann die Gruppierung, die das maximale Datum.

+0

Danke Gordon. Das ist eine interessante Alternative - wird damit auch spielen. Viel lernen heute :-) – Luke