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

Oracle 11g中用PL/SQL计算各产品在各工位的停留天数

问题描述

现有日志表数据如下:

prod_idstation_iddate_in
p1s12022-09-01 12:06:41.6216195
p2s12022-09-02 10:06:14.6216195
p2s22022-09-02 02:04:55.6216195
p1s22022-09-02 11:06:40.6216195
p3s12022-09-02 04:06:23.6216195
p1s32022-09-03 12:00:33.6216195
p2s12022-09-04 02:06:44.6216195
p1s42022-09-04 07:12:20.6216195
p2s22022-09-05 03:04:21.6216195
p2s32022-09-07 05:17:35.6216195
p1s32022-09-08 14:50:54.6216195
p1s42022-09-10 09:08:10.6216195
p1s52022-09-11 11:22:47.6216195

需求为计算每个产品在每个工位的总停留天数(以天为单位),规则如下:

  • 产品每条记录的停留时间 = 该产品下一条记录的date_in - 当前记录的date_in
  • 产品最后一条记录的停留时间 = 固定日期2022-09-13(替代sysdate) - 当前记录的date_in
  • 日志表仅能按date_in排序,工位无固定顺序
  • 最终结果按产品汇总各工位累计停留天数,未停留的工位显示0,格式示例:

| prod_id | s1 | s2 | s3 | s4 | s5 | s6 |...
| -------- | -- | -- | -- | -- | -- | -- |...
| p1 | 1 | 1 | 3 | 4 | 2 | 0 |...
| p2 | 1 | 4 | 0 | 0 | 0 | 0 |...
| p3 | 11 | 0 | 0 | 0 | 0 | 0 |...

当前使用Oracle 11g,如何用PL/SQL实现该需求?

实现方案

Oracle 11g中需要结合分析函数计算停留时间,再通过动态SQL实现动态列转置(因为工位不固定),具体步骤如下:

步骤1:计算每条记录的停留天数

先用LEAD()分析函数获取每个产品的下一条记录时间,再计算停留天数(用TRUNC()对日期差取整):

WITH prod_stay AS (
    SELECT 
        prod_id,
        station_id,
        date_in,
        -- 获取当前产品的下一条记录时间,无后续记录则用指定日期
        LEAD(date_in, 1, TO_DATE('2022-09-13', 'YYYY-MM-DD')) 
            OVER (PARTITION BY prod_id ORDER BY date_in) AS next_date
    FROM log_table
),
stay_days AS (
    SELECT 
        prod_id,
        station_id,
        -- 计算停留天数并取整
        TRUNC(next_date - date_in) AS days_stayed
    FROM prod_stay
)
SELECT * FROM stay_days;

这段代码会得到每个产品在每个工位的单次停留天数,为后续汇总转置做准备。

步骤2:动态生成工位列的转置SQL

由于工位数量不固定,需先查询所有存在的工位,动态构建PIVOT语句的列:

DECLARE
    v_cols VARCHAR2(4000);
    v_sql VARCHAR2(4000);
BEGIN
    -- 收集所有工位,拼接成PIVOT所需的列格式
    SELECT LISTAGG('''' || station_id || ''' AS ' || station_id, ', ')
        WITHIN GROUP (ORDER BY station_id)
    INTO v_cols
    FROM (SELECT DISTINCT station_id FROM log_table);

    -- 构建完整的动态SQL
    v_sql := '
        WITH prod_stay AS (
            SELECT 
                prod_id,
                station_id,
                date_in,
                LEAD(date_in, 1, TO_DATE(''2022-09-13'', ''YYYY-MM-DD'')) 
                    OVER (PARTITION BY prod_id ORDER BY date_in) AS next_date
            FROM log_table
        ),
        stay_days AS (
            SELECT 
                prod_id,
                station_id,
                TRUNC(next_date - date_in) AS days_stayed
            FROM prod_stay
        )
        SELECT *
        FROM (
            SELECT prod_id, station_id, days_stayed FROM stay_days
        )
        PIVOT (
            SUM(days_stayed) FOR station_id IN (' || v_cols || ')
        )
        ORDER BY prod_id';

    -- 执行动态SQL
    EXECUTE IMMEDIATE v_sql;
    -- 若需输出结果到屏幕,可添加DBMS_OUTPUT相关逻辑;若需保存结果,可插入到临时表
END;
/

补充说明

  1. 若要使用系统当前日期sysdate替代固定日期,只需把TO_DATE('2022-09-13', 'YYYY-MM-DD')替换为SYSDATE即可。
  2. 若工位数量较多(超过LISTAGG的长度限制),可改用XMLAGG拼接列名:
SELECT RTRIM(XMLAGG(XMLELEMENT(E, '''' || station_id || ''' AS ' || station_id, ', ') ORDER BY station_id).EXTRACT('//text()'), ', ')
INTO v_cols
FROM (SELECT DISTINCT station_id FROM log_table);
  1. 若需要强制显示所有工位(包括未出现的工位如s6),需先维护一个工位维度表,关联后再进行转置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:30:53