AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 44784
Accepted
SqlSandwiches
SqlSandwiches
Asked: 2013-06-19 10:55:42 +0800 CST2013-06-19 10:55:42 +0800 CST 2013-06-19 10:55:42 +0800 CST

Tentando encontrar a última vez que um valor foi alterado

  • 772

Eu tenho uma tabela que tem um ID, um valor e uma data. Há muitos IDs, Valores e datas nesta tabela.

Os registros são inseridos nesta tabela periodicamente. O ID permanecerá sempre o mesmo, mas ocasionalmente o valor mudará.

Como posso escrever uma consulta que me dará o ID mais a hora mais recente em que o valor foi alterado? Nota: o valor sempre aumentará.

A partir destes dados de exemplo:

  Create Table Taco
 (  Taco_ID int,
    Taco_value int,
    Taco_date datetime)

Insert INTO Taco 
Values (1, 1, '2012-07-01 00:00:01'),
        (1, 1, '2012-07-01 00:00:02'),
        (1, 1, '2012-07-01 00:00:03'),
        (1, 1, '2012-07-01 00:00:04'),
        (1, 2, '2012-07-01 00:00:05'),
        (1, 2, '2012-07-01 00:00:06'),
        (1, 2, '2012-07-01 00:00:07'),
        (1, 2, '2012-07-01 00:00:08')

O resultado deve ser:

Taco_ID      Taco_date
1            2012-07-01 00:00:05

(Porque 00:05 foi a última vez que Taco_Valuemudou.)

sql-server sql-server-2008-r2
  • 8 8 respostas
  • 43515 Views

8 respostas

  • Voted
  1. Best Answer
    Aaron Bertrand
    2013-06-19T11:09:58+08:002013-06-19T11:09:58+08:00

    Essas duas consultas baseiam-se na suposição de que Taco_valuesempre aumenta com o tempo.

    ;WITH x AS
    (
      SELECT Taco_ID, Taco_date,
        dr = ROW_NUMBER() OVER (PARTITION BY Taco_ID, Taco_Value ORDER BY Taco_date),
        qr = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date)
      FROM dbo.Taco
    ), y AS
    (
      SELECT Taco_ID, Taco_date,
        rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID, dr ORDER BY qr DESC)
      FROM x WHERE dr = 1
    )
    SELECT Taco_ID, Taco_date
    FROM y 
    WHERE rn = 1;
    

    Uma alternativa com menos loucura de função de janela:

    ;WITH x AS
    (
      SELECT Taco_ID, Taco_value, Taco_date = MIN(Taco_date)
      FROM dbo.Taco
      GROUP BY Taco_ID, Taco_value
    ), y AS
    (
      SELECT Taco_ID, Taco_date, 
        rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
      FROM x
    )
    SELECT Taco_ID, Taco_date FROM y WHERE rn = 1;
    

    Exemplos no SQLfiddle


    Atualizar

    Para aqueles que acompanham, houve disputa sobre o que aconteceria se Taco_valuepudesse se repetir. Se puder ir de 1 a 2 e depois voltar a 1 para qualquer dado Taco_ID, as consultas não funcionarão. Aqui está uma solução para esse caso, mesmo que não seja exatamente a técnica de lacunas e ilhas que alguém como Itzik Ben-Gan possa sonhar, e mesmo que não seja relevante para o cenário do OP - pode ser relevante para um futuro leitor. É um pouco mais complexo e também adicionei uma variável adicional - uma Taco_IDque só tem um Taco_value.

    Se você quiser incluir a primeira linha para qualquer ID em que o valor não mudou em todo o conjunto:

    ;WITH x AS
    (
      SELECT *, rn = ROW_NUMBER() OVER 
        (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
      FROM dbo.Taco
    ), rest AS (SELECT * FROM x WHERE rn > 1)
    SELECT  
      main.Taco_ID, 
      Taco_date = MIN(CASE 
        WHEN main.Taco_value = rest.Taco_value 
        THEN rest.Taco_date ELSE main.Taco_date 
      END)
    FROM x AS main LEFT OUTER JOIN rest
    ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
    WHERE main.rn = 1
    AND NOT EXISTS 
    (
      SELECT 1 FROM rest AS rest2
       WHERE Taco_ID = rest.Taco_ID
       AND rn < rest.rn
       AND Taco_value <> rest.Taco_value
    ) 
    GROUP BY main.Taco_ID;
    

    Se você deseja excluir essas linhas, é um pouco mais complexo, mas ainda com pequenas alterações:

    ;WITH x AS
    (
      SELECT *, rn = ROW_NUMBER() OVER 
        (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
      FROM dbo.Taco
    ), rest AS (SELECT * FROM x WHERE rn > 1)
    SELECT 
      main.Taco_ID, 
      Taco_date = MIN(
      CASE 
        WHEN main.Taco_value = rest.Taco_value 
        THEN rest.Taco_date ELSE main.Taco_date 
      END)
    FROM x AS main INNER JOIN rest -- ***** change this to INNER JOIN *****
    ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
    WHERE main.rn = 1
    AND NOT EXISTS
    (
      SELECT 1 FROM rest AS rest2
       WHERE Taco_ID = rest.Taco_ID
       AND rn < rest.rn
       AND Taco_value <> rest.Taco_value
    )
    AND EXISTS -- ***** add this EXISTS clause ***** 
    (
      SELECT 1 FROM rest AS rest2
       WHERE Taco_ID = rest.Taco_ID
       AND Taco_value <> rest.Taco_value
    )
    GROUP BY main.Taco_ID;
    

    Exemplos de SQLfiddle atualizados

    • 13
  2. Andriy M
    2013-06-20T01:04:49+08:002013-06-20T01:04:49+08:00

    Basicamente, esta é a sugestão de @Taryn "condensada" em um único SELECT sem tabelas derivadas:

    SELECT DISTINCT
      Taco_ID,
      Taco_date = MAX(MIN(Taco_date)) OVER (PARTITION BY Taco_ID)
    FROM Taco
    GROUP BY
      Taco_ID,
      Taco_value
    ;
    

    Nota: esta solução tem em conta a estipulação que Taco_valuesó pode aumentar. (Mais exatamente, ele assume que Taco_valuenão pode voltar para um valor anterior - o mesmo que a resposta vinculada, na verdade.)

    Uma demonstração do SQL Fiddle para a consulta: http://sqlfiddle.com/#!3/91368/2

    • 13
  3. Taryn
    2013-06-19T11:15:32+08:002013-06-19T11:15:32+08:00

    Você deve ser capaz de usar as duas funções min()e max()agregar o resultado:

    select t1.Taco_ID, MAX(t1.taco_date) Taco_Date
    from taco t1
    inner join
    (
        select MIN(taco_date) taco_date,
            Taco_ID, Taco_value
        from Taco
        group by Taco_ID, Taco_value
    ) t2
        on t1.Taco_ID = t2.Taco_ID
        and t1.Taco_date = t2.taco_date
    group by t1.Taco_Id
    

    Veja SQL Fiddle com demonstração

    • 7
  4. ypercubeᵀᴹ
    2013-06-19T12:29:46+08:002013-06-19T12:29:46+08:00

    Mais uma resposta baseada na suposição de que os valores não reaparecem (isso é basicamente a consulta 2 do @Aaron, condensada em um ninho a menos):

    ;WITH x AS
    (
      SELECT 
        Taco_ID, Taco_value, 
        Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                                ORDER BY MIN(Taco_date) DESC),
        Taco_date = MIN(Taco_date) 
      FROM dbo.Taco
      GROUP BY Taco_ID, Taco_value
    )
    SELECT Taco_ID, Taco_value, Taco_date
    FROM x 
    WHERE Rn = 1 ;
    

    Teste em: SQL-Fiddle


    E uma resposta para o problema mais geral, onde os valores podem reaparecer:

    ;WITH x AS
    (
      SELECT 
        Taco_ID, Taco_value, 
        Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                                ORDER BY MAX(Taco_date) DESC),    
        Taco_date = MAX(Taco_date) 
      FROM dbo.Taco
      GROUP BY Taco_ID, Taco_value
    )
    SELECT t.Taco_ID, Taco_date = MIN(t.Taco_date)
    FROM x
      JOIN dbo.Taco t
        ON  t.Taco_ID = x.Taco_ID
        AND t.Taco_date > x.Taco_date
    WHERE x.Rn = 2 
    GROUP BY t.Taco_ID ;
    

    (ou usando CROSS APPLYpara que toda a linha relacionada, incluindo o value, seja mostrada):

    ;WITH x AS
    (
      SELECT 
        Taco_ID, Taco_value, 
        Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                                ORDER BY MAX(Taco_date) DESC),    
        Taco_date = MAX(Taco_date) 
      FROM dbo.Taco
      GROUP BY Taco_ID, Taco_value
    )
    SELECT t.*
    FROM x
      CROSS APPLY 
      ( SELECT TOP (1) *
        FROM dbo.Taco t
        WHERE t.Taco_ID = x.Taco_ID
          AND t.Taco_date > x.Taco_date
        ORDER BY t.Taco_date
      ) t
    WHERE x.Rn = 2 ;
    

    Teste em: SQL-Fiddle-2

    • 5
  5. Kenneth Fisher
    2013-06-19T11:17:04+08:002013-06-19T11:17:04+08:00

    FYI +1 para fornecer estrutura e dados de amostra. A única coisa que eu poderia ter pedido é a saída esperada para esses dados.

    EDIT: Este ia me deixar louco. Acabei de saber que havia uma maneira "simples" de fazer isso. Eu me livrei das soluções incorretas e coloquei uma que acredito estar correta. Aqui está uma solução semelhante a @bluefeets, mas abrange os testes que @AaronBertrand deu.

    ;WITH TacoMin AS (SELECT Taco_ID, Taco_value, MIN(Taco_date) InitialValueDate
                    FROM Taco
                    GROUP BY Taco_ID, Taco_value)
    SELECT Taco_ID, MAX(InitialValueDate)
    FROM TacoMin
    GROUP BY Taco_ID
    
    • 2
  6. JJ_Coder4Hire
    2018-09-21T21:39:22+08:002018-09-21T21:39:22+08:00

    Por que não apenas obter a diferença do valor do atraso e do valor do lead? se a diferença for zero, não mudou, é diferente de zero, então mudou. Isso pode ser feito em uma consulta simples:

    -- example gives the times the value changed in the last 24 hrs
    SELECT
        LastUpdated, [DiffValue]
    FROM (
      SELECT
          LastUpdated,
          a.AboveBurdenProbe1TempC - coalesce(lag(a.AboveBurdenProbe1TempC) over (order by ProcessHistoryId), 0) as [DiffValue]
      FROM BFProcessHistory a
      WHERE LastUpdated > getdate() - 1
    ) b
    WHERE [DiffValue] <> 0
    ORDER BY LastUpdated ASC
    
    • 1
  7. user2846273
    2020-02-29T15:14:31+08:002020-02-29T15:14:31+08:00

    Tive um problema semelhante hoje - e no Power BI eu poderia ter resolvido com o uso de Tabler.FillDown. Um pouco de pesquisa me levou a uma variante SQL de FillDown:

    https://www.oraylis.de/blog/fill-down-table-in-t-sql-last-non-empty-value

    Então, dediquei um tempo para adaptar essa solução a este exemplo - com uma linha extra adicionada para mostrar a reutilização de Taco_value.

    Insert INTO Taco 
    Values (1, 1, '2012-07-01 00:00:09')
    

    Nota: esta solução leva em consideração que Taco_value pode aumentar E diminuir (ou voltar ao valor anterior)

    WITH help1
     AS (SELECT 
             *
            ,[ChangeIndicator] = CASE
                                     WHEN [Taco_value] = LAG([Taco_value],1) OVER(
                                          ORDER BY 
             [Taco_ID]) THEN 0
                                     ELSE 1
                                 END
            ,[Taco_lag] = LAG([Taco_value],1) OVER(
             ORDER BY 
             [Taco_date])
         FROM [Taco]),
     help2
     AS (SELECT 
             *
            ,[RowGroup] = SUM([ChangeIndicator]) OVER(
             ORDER BY 
             [Taco_date])
         FROM [help1])
     SELECT 
         *
        ,[ChangeDate] = FIRST_VALUE([Taco_date]) OVER(PARTITION BY [RowGroup]
         ORDER BY 
         [Taco_date])
        ,[Taco_FillDown] = FIRST_VALUE([Taco_lag]) OVER(PARTITION BY [RowGroup]
         ORDER BY 
         [Taco_date])
     FROM [help2]
     ORDER BY 
         [Taco_date]
    

    Resultado: insira a descrição da imagem aqui

    • 0
  8. pmc086
    2013-06-19T22:36:20+08:002013-06-19T22:36:20+08:00

    Isso poderia ser tão simples quanto o seguinte?

           SELECT taco_id, MAX(
                 CASE 
                     WHEN taco_value <> MAX(taco_value) 
                     THEN taco_date 
                     ELSE null 
                 END) AS last_change_date
    

    Dado que taco_value sempre aumenta?

    ps Eu sou bastante iniciante em SQL, no entanto, aprendendo devagar, mas com segurança.

    • -1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Conceder acesso a todas as tabelas para um usuário

    • 5 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve