Niet-relationele oplossing
Ik denk niet dat een van de andere antwoorden juist is.
-
GROUP BY
werkt niet -
ROW_NUMBER()
gebruiken dwingt de gegevens in een Record Filing System-structuur, die fysiek is, en verwerkt deze vervolgens als fysieke records. Tegen enorme prestatiekosten. Om zo'n code te schrijven, moet je natuurlijk nadenken in termen van RFS in plaats van in relationele termen te denken. -
Het gebruik van CTE's is hetzelfde. Itereren door de gegevens, vooral gegevens die niet veranderen. Tegen een iets andere enorme prijs.
-
Cursors zijn absoluut het verkeerde ding om een andere reeks redenen. (a) Cursors hebben code nodig en u hebt een View aangevraagd. (b) Cursors verlaten de set-processing-engine en keren terug naar rij-voor-rij-verwerking. Nogmaals, niet vereist. Als een ontwikkelaar in een van mijn teams cursors of tijdelijke tabellen gebruikt in een relationele database (dus niet in een archiefsysteem), schiet ik ze.
Relationele oplossing
-
Uw gegevens is relationeel, logisch, de twee gegeven gegevens kolommen zijn alles wat nodig is.
-
Natuurlijk moeten we een View (afgeleide relatie) vormen om het gewenste rapport te verkrijgen, maar dat bestaat uit pure SELECT's, wat heel wat anders is dan verwerken (het converteren naar een bestand , wat fysiek is, en vervolgens het bestand . verwerken; of tijdelijke tabellen; of werktafels; of CTE's; of ROW_Number(); enz.).
-
In tegenstelling tot de klaagzangen van "theoretici", die een agenda hebben, gaat SQL prima om met relationele gegevens. En uw gegevens zijn relationeel.
Zorg daarom voor een relationele mentaliteit, een relationele kijk op de gegevens en een mentaliteit voor het verwerken van sets. Aan elke rapportagevereiste over een relationele database kan worden voldaan met een enkele SELECT. Het is niet nodig om terug te gaan naar ISAM-bestandsverwerkingsmethoden van vóór 1970.
Ik ga ervan uit dat de primaire sleutel (de reeks kolommen die een relationele rij-uniciteit geven) Date,
is en op basis van de gegeven voorbeeldgegevens is het gegevenstype DATE.
Probeer dit:
CREATE VIEW MyTable_Base_V -- Foundation View
AS
SELECT Date,
Date_Next,
Price
FROM (
-- Derived Table: project rows with what we need
SELECT Date,
[Date_Next] = DATEADD( DD, 1, O.Date ),
Price,
[Price_Next] = (
SELECT Price -- NULL if not exists
FROM MyTable
WHERE Date = DATEADD( DD, 1, O.Date )
)
FROM MyTable MT
) AS X
WHERE Price != Price_Next -- exclude unchanging rows
GO
CREATE VIEW MyTable_V -- Requested View
AS
SELECT [Date_From] = (
-- Date of the previous row
SELECT MAX( Date_Next ) -- previous row
FROM MyTable_V
WHERE Date_Next < MT.Date
),
[Date_To] = Date, -- this row
Price
FROM MyTable_Base_V MT
GO
SELECT *
FROM MyTable_V
GO
Methode, algemeen
Dit is natuurlijk een methode, daarom is het generiek, het kan worden gebruikt om de From_
te bepalen en To_
van elk gegevensbereik (hier een Date,
bereik), op basis van een gegevenswijziging (hier een wijziging in Price
).
Hier, uw Dates
zijn opeenvolgend, dus de bepaling van Date_Next
is eenvoudig:verhoog de Date,
met 1 dag. Als de PK toeneemt maar niet opeenvolgend (bijv. DateTime
of TimeStamp
of een andere sleutel), verander de afgeleide tabel X
naar:
-- Derived Table: project rows with what we need
SELECT DateTime,
[DateTime_Next] = (
-- first row > this row
SELECT TOP 1
DateTime -- NULL if not exists
FROM MyTable
WHERE DateTime > MT.DateTime
),
Price,
[Price_Next] = (
-- first row > this row
SELECT TOP 1
Price -- NULL if not exists
FROM MyTable
WHERE DateTime > MT.DateTime
)
FROM MyTable MT
Geniet ervan.
Voel je vrij om commentaar te geven, vragen te stellen, enz.