AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 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

窗口函数获取记录对并计算时间差

  • 772

我正在使用以下示例中的事件数据集:

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

数据集按事件的时间戳排序,数据随着事件的实时发生而增长。数据集中可以随时包含更多事件类型(不仅限于示例中的 A、B、C、D),并且同一事件类型可以多次出现在表中。Is_Active 布尔字段用作指示事件是否仍处于活动状态 (1) 或不处于活动状态 (0) 的一种方式。

从这个意义上说,我尝试使用 SQL 窗口函数对这些数据进行一些转换。我不一定限于特定的产品或技术,因此请随时告诉您如何解决以下问题。

我想要做的是当它们具有相反的 Is_Active 值时动态配对相同类型的每个事件,然后获取该事件的活动时间。换句话说,给定一个事件 X,我需要在 Is_Active 第一次为 1 时获取它的 Event_Timestamp(Begin_Timestamp),然后使用 Is_Active 1 忽略此事件 X 的其余行,直到我得到 Is_Active 0 ,所以我可以再次获取 Event_Timestamp (End_Timestamp)。然后,当我再次在 Is_Active 列中发现事件 X 为 1 时,我将继续应用此逻辑。

结果表的一个示例是:

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

是否有任何窗口函数可以帮助我获取这些事件对,以便我可以计算每个事件的持续时间?

sql-server postgresql
  • 3 3 个回答
  • 386 Views

3 个回答

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

    你需要类似的东西

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

    当然,您可以使用一个计算所有窗口值的 CTE 将其组合到查询中。上面的long way是用来解释算法的。

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

    鉴于数据

    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);
    

    您的结果将使用一些阶段进行计算(每个阶段都在 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
    

    如果您的数据还包含多个结束事件,则必须在案例语句中调整对结束事件的识别。这将产生您在帖子中预期的结果:

    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
    

    不需要任何 Window Function.

    你可以用不同的样本数据测试我的脚本,也可以给我性能测试的场景。

    事实上你应该提供Real Table Schema.

    • 0

相关问题

  • 存储过程可以防止 SQL 注入吗?

  • 死锁的主要原因是什么,可以预防吗?

  • PostgreSQL 中 UniProt 的生物序列

  • 如何确定是否需要或需要索引

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

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

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve