Soluzione non relazionale
Non credo che nessuna delle altre risposte sia corretta.
-
GROUP BY
non funzionerà -
Utilizzando
ROW_NUMBER()
forza i dati in una struttura del sistema di archiviazione dei record, che è fisica, e quindi li elabora come record fisici. Ad un costo di prestazioni enorme. Ovviamente, per scrivere un codice del genere, ti obbliga a pensare in termini di RFS invece di pensare in termini relazionali. -
L'uso dei CTE è lo stesso. Iterazione attraverso i dati, in particolare i dati che non cambiano. A un costo enorme leggermente diverso.
-
I cursori sono sicuramente la cosa sbagliata per una serie di motivi diversi. (a) I cursori richiedono il codice e tu hai richiesto una vista (b) I cursori abbandonano il motore di elaborazione delle impostazioni e tornano all'elaborazione riga per riga. Ancora una volta, non richiesto. Se uno sviluppatore di uno dei miei team utilizza cursori o tabelle temporanee su un database relazionale (cioè non un sistema di archiviazione dei record), gli sparo.
Soluzione relazionale
-
I tuoi dati è Relazionale, logico, i due dati dati le colonne sono tutto ciò che è necessario.
-
Certo, dobbiamo formare una vista (relazione derivata), per ottenere il rapporto desiderato, ma che consiste in SELECT puri, che è abbastanza diverso dall'elaborazione (convertendolo in un file , che è fisico, e quindi elabora il file; o tavoli temporanei; o tavoli da lavoro; o CTE; o RIGA_Numero(); ecc).
-
Contrariamente alle lamentele dei "teorici", che hanno un'agenda, SQL gestisce perfettamente i dati relazionali. E i tuoi dati sono relazionali.
Pertanto, mantieni una mentalità relazionale, una visione relazionale dei dati e una mentalità di elaborazione degli insiemi. Ogni requisito di report su un database relazionale può essere soddisfatto utilizzando un unico SELECT. Non è necessario regredire ai metodi di gestione dei file ISAM precedenti al 1970.
Presumo che la chiave primaria (l'insieme di colonne che danno un'unicità di riga relazionale) sia Date,
e in base ai dati di esempio forniti, il tipo di dati è DATE.
Prova questo:
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
Metodo, generico
Ovviamente questo è un metodo, quindi è generico, può essere usato per determinare il From_
e To_
di qualsiasi intervallo di dati (qui, una Dates
range), in base a qualsiasi modifica dei dati (qui, una modifica in Price
).
Qui, le tue Dates
sono consecutive, quindi la determinazione di Date_Next
è semplice:incrementa la Date
entro 1 giorno. Se il PK è in aumento ma non consecutivi (es. DateTime
o TimeStamp
o qualche altra chiave), cambia la tabella derivata X
a:
-- 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
Divertiti.
Sentiti libero di commentare, porre domande, ecc.