Solution non relationnelle
Je ne pense pas qu'aucune des autres réponses ne soit correcte.
-
GROUP BY
ne fonctionnera pas -
Utilisation de
ROW_NUMBER()
force les données dans une structure de système de classement des enregistrements, qui est physique, puis les traite comme des enregistrements physiques. À un coût de performance énorme. Bien sûr, pour écrire un tel code, cela vous oblige à réfléchir en termes de RFS au lieu de penser en termes relationnels. -
L'utilisation des CTE est la même. Itérer à travers les données, en particulier les données qui ne changent pas. À un coût massif légèrement différent.
-
Les curseurs sont certainement la mauvaise chose pour un ensemble de raisons différentes. (a) Les curseurs nécessitent du code et vous avez demandé une vue (b) Les curseurs abandonnent le moteur de traitement d'ensemble et reviennent au traitement ligne par ligne. Encore une fois, pas obligatoire. Si un développeur de l'une de mes équipes utilise des curseurs ou des tables temporaires sur une base de données relationnelle (c'est-à-dire pas un système de classement d'enregistrements), je les tire.
Solution relationnelle
-
Vos données est Relationnel, logique, les deux données données les colonnes suffisent.
-
Bien sûr, nous devons former une vue (relation dérivée), pour obtenir le rapport souhaité, mais cela consiste en des SELECT purs, ce qui est assez différent du traitement (le convertir en un fichier , qui est physique, puis traitement du fichier; ou tables temporaires ; ou tables de travail ; ou CTE ; ou ROW_Number(); etc.).
-
Contrairement aux lamentations des "théoriciens", qui ont un agenda, SQL gère parfaitement les données relationnelles. Et vos données sont relationnelles.
Par conséquent, maintenez un état d'esprit relationnel, une vision relationnelle des données et une mentalité de traitement d'ensemble. Chaque exigence de rapport sur une base de données relationnelle peut être satisfaite à l'aide d'un seul SELECT. Il n'est pas nécessaire de revenir aux méthodes de gestion des fichiers ISAM antérieures à 1970.
Je supposerai que la clé primaire (l'ensemble de colonnes qui donnent l'unicité d'une ligne relationnelle) est Date,
et sur la base des exemples de données donnés, le type de données est DATE.
Essayez ceci :
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
Méthode, Générique
Bien sûr c'est une méthode, donc elle est générique, elle peut être utilisée pour déterminer le From_
et To_
de n'importe quelle plage de données (ici, une Date
gamme), en fonction de tout changement de données (ici, un changement de Price
).
Ici, vos Dates
sont consécutifs, donc la détermination de Date_Next
est simple :incrémentez la Date
d'ici 1 jour. Si le PK augmente mais pas consécutifs (ex. DateTime
ou TimeStamp
ou une autre clé), modifiez la table dérivée X
à :
-- 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
Profitez-en.
N'hésitez pas à commenter, poser des questions, etc.