【问题标题】:Used data from the previous record to calculate data of the current one使用上一条记录的数据来计算当前记录的数据
【发布时间】:2016-09-29 04:05:29
【问题描述】:

AS400 的 LAG 和 LEAD 语法是什么? 我正在尝试根据之前的记录对当前记录进行一些计算?

例如: 我需要根据前一条记录计算每条记录的工作中心数量。

更新:

重要的是,正如我在示例中所描述的,计算将基于前一行而不是所有前行的总和。我只是给出了一个非常优化的例子来说明我的需求,但实际上减去所有以前记录的 SUM 对我没有帮助。有没有办法只订阅以前的记录?

【问题讨论】:

  • 数据中有没有排序方式或者主键可以用来识别之前的记录?
  • 我猜,这几乎是相似的 - stackoverflow.com/questions/32066074/…
  • @AT-2016,这是针对 sql server 的。不确定,它是否对我有用。
  • @ehh 你到底为什么用 DB2 来标记这个问题?
  • @TimBiegeleisen,DB2 不是 DB2/400 吗?

标签: sql db2 ibm-midrange


【解决方案1】:

您正在计算运行总数,LAGLEAD 不一定能帮助您完成。计算运行总计的一种方法是使用相关子查询:

SELECT t1.*,
       t1.total_quantity - (SELECT SUM(t2.scrap_quantity)
                            FROM yourTable t2
                            WHERE t2.work_center <= t1.work_center) AS work_center_quantity
FROM yourTable t1
ORDER BY t1.work_center

【讨论】:

    【解决方案2】:

    首先,使用正确的平台名称,您将更有可能找到当前的参考资料。 IBM i 文档位于Knowledge Center

    其次,DB2 共有三个家族。 IBM i 系列还没有 LEAD 和 LAG。 (其他系列是 LUW(Linux、Unix、Windows)和 DB2 for z - 大型机)。

    该请求有点不寻常,因为 SQL 集处理往往侧重于集而不是相对记录数。因此,不能使用递归 CTE,因为没有简单的父/子关系。但是,可以应用蛮力。

    create table so (
      WorkCenter char(5),
      TotalQuantity decimal(7, 0),
      ScrapQuantity decimal(7, 0),
      WorkCenterQuantity decimal(7, 0)
      );
    
    insert into so values ('A', 1000, 10, 990);
    insert into so values ('B', 1000, 5, 985);
    insert into so values ('C', 1000, 12, 973);
    insert into so values ('D', 1000, 1, 972);
    
    select 1 as level, WorkCenter, TotalQuantity, ScrapQuantity, WorkCenterQuantity, TotalQuantity - ScrapQuantity as newQUantity
    from so
    where rrn(so) = 1
     union all
    select rrn(o) as level, o.WorkCenter, o.TotalQuantity, o.ScrapQuantity, o.WorkCenterQuantity, 
      (select i.WorkCenterQuantity
       from so i
       where rrn(i) = rrn(o) - 1) - o.ScrapQuantity as NewWorkCenterQuantity
    from so o
    where rrn(o) > 1
    order by 1;
    

    我们的想法是获取第一行,它使用当前行中的列进行计算,并将其与后续行进行 UNION,后者使用当前行和上一行进行计算。

    编辑假设 WorkCenterQuantity 为零。在计算中使用@Tim 的总和。

    update so
    set WorkCenterQuantity = 0;
    
    update so u
    set WorkCenterQuantity = (
      select NewWorkCenterQuantity 
      from 
        (select 1 as level, WorkCenter, TotalQuantity, ScrapQuantity, WorkCenterQuantity, TotalQuantity - ScrapQuantity as NewWorkCenterQuantity
         from so
         where rrn(so) = 1 
          union all
         select rrn(o) as level, o.WorkCenter, o.TotalQuantity, o.ScrapQuantity, o.WorkCenterQuantity, 
           (select TotalQuantity
            from so
            where rrn(so) = 1) 
             - 
           (select sum(i.ScrapQuantity)
            from so i
            where rrn(i) <= rrn(o)) as NewWorkCenterQuantity
         from so o
         where rrn(o) > 1
         ) x 
      where u.WorkCenter = x.WorkCenter)
    ;
    

    这个想法是使用第 n+1 行的运行总计执行计算,但从第 1 行检索初始数量。

    【讨论】:

    • WorkCenterQuantity 是要计算的列。在您的创建表中添加要计算的值。
    • 看起来你真的想要@Tim 的答案。计算不能基于前一行,因为前一行 - 就像所有行一样 - 在更新之前将为零。所以你真正想要的是计算基于之前的计算,这就是运行总计。见编辑。
    • 我们快到了:我得到 A(990)、B(972)、C(972)、D(972)。我也会尝试修复它。
    【解决方案3】:

    也许以下内容与我对DB2 recursive UDF Table 的回复非常相似,但对于这个回复,我几乎没有进行任何评论,除了使用给定样本数据的测试结果[加上一个用于在视觉上表达与预期输出等价的列] 来验证:

    设置:

    set current schema <whatever>
    create table wc
    ( work_center    for wc       char     
    , total_quantity for tq       dec      
    , scrap_quantity for sq       dec      
    , vfy_quantity   for vq       dec
    ) 
    ;
    insert into  wc       values     
      ( 'A' ,   1000,     10,   990 )
    , ( 'B' ,   1000,      5,   985 )
    , ( 'C' ,   1000,     12,   973 )
    , ( 'D' ,   1000,      1,   972 )
    ;
    

    查询和结果:

    with
      ordRows (rn, wc, tq, sq, vq) as
       ( select row_number() over(order by wc) as rn
              ,    wc, tq, sq, vq 
         from wc
       )    
    , subRows (rn, wc, tq, sq, vq, cq /* calc qty */) as
       ( select rn, wc, tq, sq, vq
              , ( tq - sq )
         from ordRows
         where rn = 1
       union all
         select b.rn, b.wc, b.tq, b.sq, b.vq        
              , ( a.cq - b.sq )
         from subRows a
         join ordRows b
           on a.rn = ( b.rn - 1 )
       )
    select dec(rn, 5) as rn, wc, tq, sq
         , vq, ' = ' as eq, cq
    from subrows
    ; -- likeness of report from above query:
    ....+....1....+....2....+....3....+....4....+....5....+
        RN   WC      TQ       SQ       VQ   EQ          CQ 
         1   A    1,000       10      990    =         990 
         2   B    1,000        5      985    =         985 
         3   C    1,000       12      973    =         973 
         4   D    1,000        1      972    =         972 
    ********  End of data  ********                        
    

    【讨论】:

    • 获取“SQL0206 - 列 VQ 不在指定表中。”错误
    • 奇数。该脚本在 IBM i 6.1、7.1、7.3 上对我来说都是有用的。从 v5r4 开始应该支持语法 IIRC,但我无权访问该版本,也无权访问 IBM i 7.2 [aka v7r2]。我没有看到任何提及正在使用的代码级别;每在 OP 中将服务器称为 AS400,我想最有可能是其中最古老的? :-(
    • 我以错误的方式创建了表格(对此感到抱歉)。我又试了一次,它就像一个魅力。太好了!
    • 假设我正在添加一个新的项目列,其中每个项目都有 A、B、C 和 D 工作中心。在这种情况下,row_number 不会为每个项目重置为 1,这与结果不匹配。如何将每个新项目的 row_number 重置为 1?
    • 可能没时间,也不了解新场景。也许开始一个新问题,提供 DDL 的脚本 [以文本而不是图像] 和样本数据的插入,以及预期的结果 [文本或图像,无论如何] 和附带的场景描述,以帮助读者理解从数据到结果的目的;足够多的数据以避免歧义。尽管如此,我希望有人能够提供这种方法。注意:请在任何 OP 中包含 DB2 平台和版本信息;例如IBM i version.release 信息,每个 DB2 for IBM i 正在使用。
    【解决方案4】:

    试试这个:

     with tmp as (
     select t.*, rownumber() over(order by work_center  ) as rang
     from t)
     select f1.*, 
     ifnull(f2.work_center  , f1.total_quantity ) - f1.scrap_quantity 
     AS work_center_quantity 
     from tmp f1 left outer join tmp f2
     on f1.rang-1=f2.rang  
    

    【讨论】:

    • 这是一个好的开始,但它不起作用。我正在努力让它发挥作用。只有第一条记录的值,其他的都是空的
    • 在对您的建议进行了一些调查后,我发现它没有考虑到之前计算的 work_center_quantity。理想的做法是用“ifnull(f2.work_center_quantity, f1.total_quantity) - f1.scrap_quantity AS work_center_quantity”替换“ifnull ...”,但表中不存在“work_center_quantity”。有没有办法实现“理想”的解决方案
    【解决方案5】:

    试试看:)

     with tmp as (
     select t.*, rownumber() over(order by work_center  ) as rang
     from t)
     select f1.*, 
     f1.total_quantity - f3.sum_scrap_quantity    
     AS work_center_quantity 
     from tmp f1 
     inner join lateral
     (
      select sum(f2.scrap_quantity ) sum_scrap_quantity     tmp f2
      where f2.rang<=f1.rang
      ) f3 on 1=1  
    

    【讨论】:

      猜你喜欢
      • 2015-04-25
      • 2015-11-12
      • 2023-01-17
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      相关资源
      最近更新 更多