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 / 237986
Accepted
d4nielfr4nco
d4nielfr4nco
Asked: 2019-05-13 20:28:51 +0800 CST2019-05-13 20:28:51 +0800 CST 2019-05-13 20:28:51 +0800 CST

Função de janela para obter pares de registros e calcular a diferença de tempo

  • 772

Estou trabalhando com um conjunto de dados de eventos como o do exemplo abaixo:

Event_Type  Event_Timestamp         Is_Active
A           2010-10-01 00:00:00     1
B           2010-10-01 00:00:01     1
A           2010-10-01 00:00:02     0
D           2010-10-01 00:00:03     1
B           2010-10-01 00:00:04     0
C           2010-10-01 00:00:05     1
A           2010-10-01 00:00:06     1
A           2010-10-01 00:00:07     1
A           2010-10-01 00:00:08     0

O conjunto de dados é ordenado pelo timestamp dos eventos e os dados crescem à medida que os eventos acontecem em tempo real. Mais tipos de eventos podem ser incluídos no conjunto de dados a qualquer momento (não limitado a apenas A, B, C, D como no exemplo) e o mesmo tipo de evento pode aparecer na tabela várias vezes. O campo booleano Is_Active serve como forma de indicar se o evento ainda está ativo (1) ou não (0).

Nesse sentido, tentei fazer algumas transformações nesses dados usando SQL Window Functions. Não estou necessariamente restrito a um produto ou tecnologia específico, portanto, sinta-se à vontade para dizer como você resolveria o problema abaixo.

O que eu quero fazer é emparelhar dinamicamente cada evento do mesmo tipo quando eles têm valores Is_Active opostos e, em seguida, obter quanto tempo esse evento estava ativo. Em outras palavras, dado um evento X, eu precisaria obter o Event_Timestamp para ele quando Is_Active tivesse 1 pela primeira vez (Begin_Timestamp) e, em seguida, ignorar o restante das linhas para este evento X com Is_Active 1 até obter Is_Active 0 , para que eu pudesse pegar o Event_Timestamp novamente (End_Timestamp). Então, eu continuaria aplicando essa lógica quando encontrasse o evento X tendo 1 na coluna Is_Active novamente.

Um exemplo da tabela resultante seria:

Event_Type  Begin_Timestamp         End_Timestamp           Duration
A           2010-10-01 00:00:00     2010-10-01 00:00:02     2 seconds
B           2010-10-01 00:00:01     2010-10-01 00:00:04     3 seconds
D           2010-10-01 00:00:03     null                    null
C           2010-10-01 00:00:05     null                    null
A           2010-10-01 00:00:06     2010-10-01 00:00:08     2 seconds

Existe alguma função de janela que me ajude a obter esses pares de eventos para que eu possa calcular a duração de cada evento?

sql-server postgresql
  • 3 3 respostas
  • 386 Views

3 respostas

  • Voted
  1. Best Answer
    Akina
    2019-05-13T21:13:33+08:002019-05-13T21:13:33+08:00

    Você precisa de algo como

    -- get previous status, use -1 value if no previous record (because NULL value needs more complex condition in cte2)
    WITH cte1 AS (SELECT Event_Type, 
                         Event_Timestamp, 
                         Is_Active, 
                         COALESCE(LAG(Is_Active) OVER (PARTITION BY Event_Type 
                                                       ORDER BY Event_Timestamp ASC), -1) prev_event
                  FROM datatable),
         -- remove records where status not altered
         cte2 AS (SELECT Event_Type, 
                         Event_Timestamp, 
                         Is_Active
                  FROM cte1
                  WHERE Is_Active != prev_event)
    -- get data we need
    SELECT Event_Type, 
           Event_Timestamp Begin_Timestamp, 
           LEAD(Event_Timestamp) OVER (PARTITION BY Event_Type 
                                       ORDER BY Event_Timestamp ASC) End_Timestamp, 
           LEAD(Event_Timestamp) OVER (PARTITION BY Event_Type 
                                       ORDER BY Event_Timestamp ASC) - Event_Timestamp Duration
    FROM cte2
    WHERE Is_Active = 1
    

    Claro que você pode combiná-lo na consulta usando um CTE que calcula todos os valores da janela. O longo caminho acima é usado para explicar o algoritmo.

    • 1
  2. Lothar Kraner
    2019-05-14T00:16:25+08:002019-05-14T00:16:25+08:00

    Dados os dados

    SELECT x.Event_Type, x.Event_Timestamp, x.Is_Active
    INTO #Data
    FROM (VALUES 
      ('A', CONVERT(DATETIME, '2010-10-01 00:00:00'), 1),
      ('B', CONVERT(DATETIME, '2010-10-01 00:00:01'), 1),
      ('A', CONVERT(DATETIME, '2010-10-01 00:00:02'), 0),
      ('D', CONVERT(DATETIME, '2010-10-01 00:00:03'), 1),
      ('B', CONVERT(DATETIME, '2010-10-01 00:00:04'), 0),
      ('C', CONVERT(DATETIME, '2010-10-01 00:00:05'), 1),
      ('A', CONVERT(DATETIME, '2010-10-01 00:00:06'), 1),
      ('A', CONVERT(DATETIME, '2010-10-01 00:00:07'), 1),
      ('A', CONVERT(DATETIME, '2010-10-01 00:00:08'), 0)
    ) x (Event_Type, Event_Timestamp, Is_Active);
    

    Seu resultado usará alguns estágios para serem calculados (cada um em um CTE):

    WITH cte_First
    AS
    (
      SELECT d.Event_Type,
             d.Event_Timestamp,
             d.Is_Active,
             -- Get the active state of the previous row ordered by the timestamp and partitioned by the event type
             Lag(d.Is_Active, 1, NULL) OVER(PARTITION BY d.Event_Type ORDER BY d.Event_Timestamp) AS Prev_Active
      FROM #Data d
    ), cte_activity
    AS
    (
      SELECT Event_Type,
             Event_Timestamp,
             Is_Active,
             Prev_Active,
             -- check if the event is a start, an end, or can just be ignored because of the same value
             CASE 
               WHEN Is_Active = Prev_Active THEN 0 -- same value
               WHEN Is_Active = 1 THEN 1 -- start
               WHEN Is_Active = 0 THEN 2 -- end
             END AS start_definition
      FROM cte_First
    ), cte_start_end
    AS
    (
      SELECT Event_Type,
             Event_Timestamp,
             Is_Active,
             Prev_Active,
             start_definition,
             -- get the event timestamp from the next row 
             -- (because we only have the start and the end events this will be the end timestamp
             -- for the start row and null for the end row)
             LEAD(Event_Timestamp, 1, NULL) OVER(PARTITION BY Event_Type ORDER BY Event_Timestamp) AS End_Timestamp
      FROM cte_activity
      WHERE start_definition > 0
    )
    SELECT Event_Type,
           Event_Timestamp,
           End_Timestamp,
           -- calculate the difference in seconds
           DATEDIFF(SECOND, Event_Timestamp, End_Timestamp) AS Duration_Seconds
    FROM cte_start_end
    -- include only the start rows
    WHERE start_definition = 1
    ORDER BY Event_Timestamp
    

    Se seus dados também contiverem mais de um evento final, você deverá ajustar o reconhecimento do evento final na instrução case. Isso produzirá o resultado que você esperava em seu post:

    Event_Type Event_Timestamp         End_Timestamp           Duration_Seconds
    ---------- ----------------------- ----------------------- ----------------
    A          2010-10-01 00:00:00.000 2010-10-01 00:00:02.000 2
    B          2010-10-01 00:00:01.000 2010-10-01 00:00:04.000 3
    D          2010-10-01 00:00:03.000 NULL                    NULL
    C          2010-10-01 00:00:05.000 NULL                    NULL
    A          2010-10-01 00:00:06.000 2010-10-01 00:00:08.000 2
    
    (5 rows affected)
    
    • 0
  3. KumarHarsh
    2019-05-14T22:42:24+08:002019-05-14T22:42:24+08:00
    create table #temp(Event_Type varchar(50), Event_Timestamp  datetime, Is_Active bit)
    insert into #temp values
     ('A','2010-10-01 00:00:00',1)
    ,('B','2010-10-01 00:00:01',1)
    ,('A','2010-10-01 00:00:02',0)
    ,('D','2010-10-01 00:00:03',1)
    ,('B','2010-10-01 00:00:04',0)
    ,('C','2010-10-01 00:00:05',1)
    ,('A','2010-10-01 00:00:06',1)
    ,('A','2010-10-01 00:00:07',1)
    ,('A','2010-10-01 00:00:08',0)
    ,('A','2010-10-01 00:00:09',1);
    
    ;
    
    WITH CTE
    AS (
        SELECT 
            a.Event_Type
            ,A.Event_Timestamp AS Begin_Timestamp
            ,b.Event_Timestamp AS End_Timestamp
            ,datediff(second, a.Event_Timestamp, b.Event_Timestamp) Duration
        FROM #temp A
        OUTER APPLY (
            SELECT TOP 1 
                b.Event_Type
                ,b.Event_Timestamp
            FROM #temp B
            WHERE a.Event_Type = b.Event_Type
                AND b.Is_Active = 0
                AND b.Event_Timestamp > a.Event_Timestamp
            ORDER BY b.Event_Timestamp
            ) b
        WHERE a.is_active = 1
        )
    SELECT Event_Type
    ,Begin_Timestamp
    ,End_Timestamp
    ,Duration
    FROM cte A
    WHERE NOT EXISTS (
            SELECT 1
            FROM cte B
            WHERE a.Event_Type = b.Event_Type
                AND b.Begin_Timestamp < a.Begin_Timestamp
                AND a.Begin_Timestamp BETWEEN b.Begin_Timestamp
                    AND b.end_Timestamp
            )
    
    
    Drop TABLE #temp
    

    Não há necessidade de nenhum Window Function.

    Você pode testar meu script com dados de amostra diferentes e também me fornecer um cenário para teste de desempenho.

    Na verdade, você deve fornecer Real Table Schema.

    • 0

relate perguntas

  • Os procedimentos armazenados impedem a injeção de SQL?

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

  • Sequências Biológicas do UniProt no PostgreSQL

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

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

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

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 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

    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
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • 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
    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