【问题标题】:how do i insert value into a column and referring the previous value at the same time of that column in a Table in Oracle?如何将值插入列并同时引用 Oracle 表中该列的前一个值?
【发布时间】:2021-09-13 06:49:41
【问题描述】:

我有一张表,我必须在 Final_value 列中插入值

date      column1  column2   Final_value   
01-01-10    5.5       Null        Null
02-01-10    4.2       Null        NULL
03-01-10    5.3       0.4         NULL
04-01-10    6.4       Null        NULL
05-01-10    7.0       0.3         NULL
06-01-10    7.7       Null        NULL

任务说明

  1. Final_value 列的第一个值将 = column1 的第一个值,即 5.5

  2. 从第 2 行开始,我们将检查 column2 是否为 Not Null
    a) 如果为 TRUE ,则​​公式为
    上一个 Final_value 列*(当前 column1 值/上一个 Column1 值)+ column2 value * (前一列值/前一个 Final_value )

    b) 如果为 FALSE,则公式为 Final_value 列的上一个值 * ( column1 的当前值/上一个 第 1 列的值)

所需输出

date      column1  column2   Final_value   
01-01-10    5.5       Null        5.5
02-01-10    4.2       Null        4.2
03-01-10    5.3       0.4         5.7
04-01-10    6.4       Null        6.883019
05-01-10    7.0       0.3         7.850943
06-01-10    7.7       Null        8.636038

【问题讨论】:

  • 到目前为止你尝试了什么?
  • 问题是你没有任何唯一值(比如Id)。您不能使用 date-1 因为如果在特定日期没有数据可能会发生什么。但是你可以使用一个过程来更新它。
  • 我被卡住了,因为我在更新时无法引用 Final_value 列的先前值。为了获取 column1 的先前值,我使用了 lag 函数并创建了一个新列 @RobertoHernandez
  • 列日期是否总是不为空?
  • 更正了公式,它应该是 4.2 * (5.3/4.2) + .4 (4.2 / 4.2) @MarmiteBomber 感谢您指出

标签: sql oracle


【解决方案1】:
declare
v_num integer := 1;
v_column1 number(8,2);
v_column2 number(8,2);
v_Final_value number(8,2);
begin
for rec in (select * from tab order by mydate)
loop
   if(v_num = 1) then
       
       update tab set Final_value = column1 where mydate = rec.mydate;     
   else
       if(rec.column2 is not null) then
          update tab set Final_value =
           v_Final_value * (v_column1/rec.column1) +
           rec.column2 * (v_column1/v_Final_value) where mydate = rec.mydate;
       else
          update tab set Final_value =
          v_Final_value * (rec.column1 / v_column1) where mydate = rec.mydate;
       end if;
   end if;
   
   
   v_num:= v_num +1;
   
   v_column1 := rec.column1;
   v_column2 := rec.column2;
   
   select final_value 
   into v_Final_value
   from tab
   where mydate = rec.mydate;
     
end loop;
end;

【讨论】:

  • 逐行更新不是很好的方法。 bulk collect 更新的行可能会更好,然后使用 forall 执行单个更新,这样会更快。每次计算只需要当前行和前一行,无需访问先前计算的存储值即可处理。
【解决方案2】:

假设您的表包含连续天并且您从最小的天开始,您可以使用递归子查询分解来获取您的预期数据。

with t1(date_d, column1, column2, Final_value) as (
 select date_d, column1, column2, column1 Final_value 
 from tab 
 where date_d = (select min(date_d) from tab)
union all
 select t2.date_d, t2.column1, t2.column2, 
 case when t2.column2 is not null then 
     t1.Final_value * (t2.column1 / t1.column1) + t2.column2 * (t1.column1 / t1.Final_value)
 else
     t1.Final_value * (t2.column1 / t1.column1)
 end as  Final_value 
 from t1
 join tab t2 on t1.date_d + 1 = t2.date_d
)
select * from t1

请注意,anchor 子查询选择日期最短的第一行和初始 Final_value

递归子查询(union all 中的第二个查询)将行与 第二天 连接起来,因此您可以清楚地描述您的公式,其中t1 引用了上一个 em> 一天和t2 当前 一天。

结果符合预期

DATE_D                 COLUMN1    COLUMN2 FINAL_VALUE
------------------- ---------- ---------- -----------
01.01.2010 00:00:00        5,5                    5,5
02.01.2010 00:00:00        4,2                    4,2
03.01.2010 00:00:00        5,3 ,4                 5,7
04.01.2010 00:00:00        6,4             6,88301887
05.01.2010 00:00:00          7 ,3          7,80724926
06.01.2010 00:00:00        7,7             8,58797418

UPDATE/INSERT 中随意使用它,但您可能会发现仅在view 中定义final_value 是最好的解决方案...

【讨论】:

    【解决方案3】:

    您可以考虑使用model 子句,该子句允许直接引用先前计算的相同度量而无需显式递归(有时执行速度比递归查询更快)。在下面的查询中cv() 是维度的当前值(即some_measure[cv() - 1] 是上一行的列measure 的值)。我还添加了nvl 来处理除以零,如果您愿意,可以将其删除。

    with  b as (
      select
        a.*,
        /*Add result column and auxiliarry numbering column to reference prevoius row*/
        cast(null as number) as final_value,
        row_number() over(order by dt asc) as rn
      from a
    )
    select *
    from b
    model
      /*Our generated ordering column*/
      dimension by (rn)
      /*Output columns apart from dimensions*/
      measures (dt, column1, column2, final_value)
      /*Ordered execution of the rules*/
      rules update sequential order (
        /*First row should have column1 value*/
        final_value[1]= column1[cv()],
        /*Other rows should have values according to formula*/
        final_value[rn > 1] order by rn asc =
          case
            when column2[cv()] is not null
            then final_value[cv() - 1]*column1[cv()]/nullif(column1[cv() - 1], 0)
              + column2[cv()]*column1[cv() - 1]/nullif(final_value[cv() - 1], 0)
            else
              final_value[cv() - 1]*column1[cv()]/nullif(column1[cv() - 1], 0)
          end
      )
    
    注册护士 | DT |第 1 列 |第 2 栏 | FINAL_VALUE -: | :-------- | ------: | ------: | ----------------------------------------------------: 1 | 2010 年 1 月 1 日 | 5.5 | | 5.5 2 | 2010 年 1 月 2 日 | 4.2 | | 4.2 3 | 2010 年 1 月 3 日 | 5.3 | .4 | 5.7 4 | 2010 年 1 月 4 日 | 6.4 | | 6.88301886792452830188679245283018867925 5 | 2010 年 1 月 5 日 | 7 | .3 | 7.80724925521350546176762661370407149951 6 | 2010 年 1 月 6 日 | 7.7 | | 8.58797418073485600794438927507447864946

    db小提琴here

    【讨论】:

    • 它工作得很好,但是我们可以用这段代码更新多个列吗?就像我有 Final_value_1 和 Final_value_2
    • @Amit 当然,您可以更新不属于 model 子句的 dimension byorder by 部分的任何列。只需将它们放入rules。唯一的先决条件是在输入数据中有该列。但是,您可以使用子查询预先添加任意数量的任何类型的列(就像我在示例中所做的那样)
    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 2022-12-04
    • 1970-01-01
    相关资源
    最近更新 更多