Solução não relacional
Acho que nenhuma das outras respostas está correta.
-
GROUP BY
não vai funcionar
-
UsandoROW_NUMBER()
força os dados em uma estrutura do sistema de arquivamento de registros, que é física, e os processa como registros físicos. A um custo de desempenho enorme. É claro que, para escrever esse código, você precisa pensar em termos de RFS em vez de pensar em termos relacionais.
-
O uso de CTEs é o mesmo. Iterando pelos dados, especialmente dados que não mudam. A um custo maciço ligeiramente diferente.
-
Cursores são definitivamente a coisa errada por um conjunto diferente de razões. (a) Os cursores exigem código e você solicitou uma Visualização (b) Os cursores abandonam o mecanismo de processamento de conjunto e revertem para o processamento de linha por linha. Novamente, não é obrigatório. Se um desenvolvedor em qualquer uma das minhas equipes usa cursores ou tabelas temporárias em um banco de dados relacional (ou seja, não em um sistema de arquivamento de registros), eu atiro neles.
Solução Relacional
-
Seus dados é relacional, lógico, os dois dados fornecidos colunas são tudo o que é necessário.
-
Claro, temos que formar uma View (relação derivada), para obter o relatório desejado, mas que consiste em SELECTs puros, o que é bem diferente de processamento (conversão para um arquivo , que é físico e, em seguida, processando o arquivo; ou tabelas temporárias; ou mesas de trabalho; ou CTE; ou ROW_Number(); etc).
-
Ao contrário das lamentações dos "teóricos", que têm uma agenda, o SQL trata perfeitamente os dados relacionais. E seus dados são relacionais.
Portanto, mantenha uma mentalidade relacional, uma visão relacional dos dados e uma mentalidade de processamento de conjuntos. Cada requisito de relatório em um banco de dados relacional pode ser atendido usando um único SELECT. Não há necessidade de regredir aos métodos de manipulação de arquivos ISAM anteriores a 1970.
Vou assumir que a Chave Primária (o conjunto de colunas que fornece uma exclusividade de linha Relacional) é
Date,
e com base nos dados de exemplo fornecidos, o Datatype é DATE.
Tente isto:
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étodo, Genérico
Claro que este é um método, portanto é genérico, pode ser usado para determinar o
From_
e To_
de qualquer intervalo de dados (aqui, uma Date
range), com base em qualquer alteração de dados (aqui, uma alteração em Price
). Aqui, suas
Dates
são consecutivos, então a determinação de Date_Next
é simples:incremente a Date
por 1 dia. Se o PK está aumentando, mas não consecutivos (por exemplo, DateTime
ou TimeStamp
ou alguma outra chave), altere a tabela derivada X
para: -- 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
Aproveitar.
Fique a vontade para comentar, tirar dúvidas, etc.