SSMS
 sql >> Base de Dados >  >> Database Tools >> SSMS

Intervalo de datas para conjunto dos mesmos dados

Solução não relacional


Acho que nenhuma das outras respostas está correta.

  • GROUP BY não vai funcionar

  • Usando ROW_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


  1. Seus dados é relacional, lógico, os dois dados fornecidos colunas são tudo o que é necessário.

  2. 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).

  3. 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.