You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于动态条件计算滚动最小值?时序事件表实时qty最小值求解

问题描述

我有如下数据表:

time        | pid  | qty | event 
---------------------+------+-----+-------
 2021-11-27 16:15:35 | 2207 |  1  | start  
 2021-11-27 16:15:12 | 2206 |  1  | stop   
 2021-11-27 16:00:11 | 2207 |  2  | stop   
 2021-11-27 15:51:43 | 2206 |  1  | start  
 2021-11-27 15:46:49 | 2206 |  4  | stop   
 2021-11-27 15:42:47 | 2206 |  4  | start  
 2021-11-27 15:41:36 | 2206 |  1  | stop   
 2021-11-27 15:41:29 | 2208 |  3  | start  
 2021-11-27 15:41:15 | 2207 |  2  | start  
 2021-11-27 15:39:58 | 2206 |  1  | start 

可通过以下SQL语句创建:

CREATE TABLE simple (
    time  TIMESTAMPTZ UNIQUE NOT NULL,
    pid   BIGINT,
    qty   BIGINT,
    event TEXT
);
INSERT INTO simple VALUES
    ('2021-11-27 16:15:35' , 2207 ,  1  , 'start'),
    ('2021-11-27 16:15:12' , 2207 ,  1  , 'stop '),  
    ('2021-11-27 16:00:11' , 2207 ,  2  , 'stop '),  
    ('2021-11-27 15:51:43' , 2206 ,  1  , 'start'),  
    ('2021-11-27 15:46:49' , 2206 ,  4  , 'stop '),  
    ('2021-11-27 15:42:47' , 2206 ,  4  , 'start'),  
    ('2021-11-27 15:41:36' , 2206 ,  1  , 'stop' ), 
    ('2021-11-27 15:41:29' , 2208 ,  3  , 'start'),  
    ('2021-11-27 15:41:15' , 2207 ,  2  , 'start'),  
    ('2021-11-27 15:39:58' , 2206 ,  1  , 'start');  

我需要针对每行对应的时间戳,计算截至该行所有处于生效状态(未触发stop)的start事件对应的最小qty值,预期输出结果如下:

time        | pid  | qty | event | min 
---------------------+------+-----+-------+-----
 2021-11-27 16:15:35 | 2207 |  1  | start | 1 -- 2207 min pid again
 2021-11-27 16:15:12 | 2206 |  1  | stop  | 3 -- 2208 min pid, only one not stopped
 2021-11-27 16:00:11 | 2207 |  2  | stop  | 1
 2021-11-27 15:51:43 | 2206 |  1  | start | 1 -- 2206 min pid again
 2021-11-27 15:46:49 | 2206 |  4  | stop  | 2
 2021-11-27 15:42:47 | 2206 |  4  | start | 2 
 2021-11-27 15:41:36 | 2206 |  1  | stop  | 2  -- 2206 stopped, now 2207 is min pid
 2021-11-27 15:41:29 | 2208 |  3  | start | 1  
 2021-11-27 15:41:15 | 2207 |  2  | start | 1  -- min pid is still 2206
 2021-11-27 15:39:58 | 2206 |  1  | start | 1  -- first
实现方案

自定义聚合函数确实是该场景的最优方案,逐行累积维护状态的性能远高于关联子查询等实现,以下是PostgreSQL下的完整可运行方案:

步骤1:定义生效任务存储类型

用于存储当前所有未触发stop的start事件的pid和qty信息

CREATE TYPE active_task AS (pid BIGINT, qty BIGINT);

步骤2:定义状态转换函数

逐行处理数据更新生效任务列表:遇到start事件就新增记录,遇到stop事件就删除同pid同qty的最早匹配记录

CREATE OR REPLACE FUNCTION update_active_tasks(state active_task[], row_time TIMESTAMPTZ, row_pid BIGINT, row_qty BIGINT, row_event TEXT)
RETURNS active_task[] AS $$
BEGIN
    IF trim(row_event) = 'start' THEN
        state := state || (row_pid, row_qty)::active_task;
    ELSIF trim(row_event) = 'stop' THEN
        FOR i IN 1..array_length(state, 1) LOOP
            IF state[i].pid = row_pid AND state[i].qty = row_qty THEN
                state := array_remove(state, state[i]);
                EXIT;
            END IF;
        END LOOP;
    END IF;
    RETURN state;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

步骤3:定义结果计算函数

从当前生效任务列表中提取最小qty值

CREATE OR REPLACE FUNCTION get_min_active_qty(state active_task[])
RETURNS BIGINT AS $$
BEGIN
    RETURN (SELECT min(qty) FROM unnest(state) AS t(qty));
END;
$$ LANGUAGE plpgsql IMMUTABLE;

步骤4:创建自定义聚合函数

CREATE AGGREGATE min_active_qty(TIMESTAMPTZ, BIGINT, BIGINT, TEXT) (
    SFUNC = update_active_tasks,
    STYPE = active_task[],
    FINALFUNC = get_min_active_qty,
    INITCOND = '{}'
);

步骤5:执行查询

按时间升序累积计算,最终按时间降序输出即可得到和预期完全一致的结果

SELECT 
    time, pid, qty, trim(event) AS event,
    min_active_qty(time, pid, qty, event) OVER (ORDER BY time ASC) AS min
FROM simple
ORDER BY time DESC;

内容的提问来源于stack exchange,提问作者blunderedbus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 02:54:07