Oracle:使用来自同一表的汇总值更新表中的值

时间:2019-05-09 09:19:19

标签: oracle sql-update

我正在寻找一种可能更好的方法。

我已经在Oracle 11.2中创建了一个临时表,用于预先计算在其他选择中需要的值,而不是总是每次选择都再次生成它们。

create global temporary table temp_foo (
    DT                  timestamp(6), --only the date part will be used in this example but for later things I will need the time
    Something           varchar2(100), 
    Customer            varchar2(100), 
    MinDate             timestamp(6), 
    MaxDate             timestamp(6),
    Filecount           int, 
    Errorcount          int,
    AvgFilecount        int,
    constraint PK_foo primary key (DT, Customer)
) on commit preserve rows;

然后我首先为AvgFilecount以外的所有内容插入一些固定值。 AvgFilecount应包含先前3条记录的Filecount的平均值(按DT中的日期)。结果将转换为int没关系,我不需要小数位

DT           | Customer | Filecount | AvgFilecount
2019-04-30   | x        | 10        | avg(2+3+9)
2019-04-29   | x        | 2         | based on values before this
2019-04-28   | x        | 3         | based on values before this
2019-04-27   | x        | 9         | based on values before this

我考虑过使用普通的UPDATE语句,因为它比循环遍历这些值要快。我应该指出,DT字段中没有空格,但是很显然第一个字段中没有以前的记录。如果我要遍历,我可以很容易地用AvgFilecount计算(the record before previous record/2 + previous record)/3,而我不能用UPDATE来计算,因为我不能保证它们的执行顺序。因此,我只取最后3条记录(由DT处理)并从那里进行计算就可以了。

我认为这是一个简单的更新,这让我头疼。我主要在做SQL Server,我只想加入其他3条记录,但是在Oracle中似乎有些不同。我找到了https://stackoverflow.com/a/2446834/4040068,并想在答案中使用第二种方法。

update 
(select curr.DT, curr.temp_foo, curr.Filecount, curr.AvgFilecount as OLD, (coalesce(Minus1.Filecount, 0) + coalesce(Minus2.Filecount, 0) + coalesce(Minus3.Filecount, 0)) / 3 as NEW
 from temp_foo curr
 left join temp_foo Minus1 ON Minus1.Customer = curr.Customer and trunc(Minus1.DT) = trunc(curr.DT-1)
 left join temp_foo Minus2 ON Minus2.Customer = curr.Customer and trunc(Minus2.DT) = trunc(curr.DT-2)
 left join temp_foo Minus3 ON Minus3.Customer = curr.Customer and trunc(Minus3.DT) = curr.DT-3
 order by 1, 2
)
set OLD = NEW;

哪个给我一个

  

ORA-01779:无法修改映射到未保留键的列   表   01779. 00000-“无法修改映射到非键保留表的列”   *原因:试图插入或更新联接视图的列,              映射到非保留键的表。   *操作:直接修改基础基表。

我认为这应该可行,因为两个连接条件都在主键中,因此是唯一的。我目前正在上述答案中采用第一种方法,但是它变得越来越大,感觉应该对此有一个更好的解决方案。

我想尝试的其他事项:

  • 使用嵌套的子选择(嵌套,因为Oracle不知道top(n),我需要对子选择进行排序)选择DT排序的前3条记录,然后用rownum <= 3进行外部选择,然后我可以只需使用AVG()。但是,有人告诉我subselect的速度可能很慢,并且在Oracle性能方面,联接更好。邓诺(Dunno)确实是这样,还没有做任何测试

编辑:我的插入内容现在看起来像这样。我已经汇总了一天的文件计数,因为每个DT的每个Customer的每个Something可以有多个记录。

insert into temp_foo (DT, Something, Customer, Filecount)
select dates.DT, tbl1.Something, tbl1.Customer, coalesce(sum(tbl3.Filecount),0)
from table(Function_Returning_Daterange(NULL, NULL)) dates
cross join
    (SELECT Something, 
        Code, 
        Value
    FROM Table2 tbl2
    WHERE (Something = 'Value')) tbl1
left outer join Table3 tbl3
    on      tbl3.Customer = tbl1.Customer
    and     trunc(tbl3.MinDate) = trunc(dates.DT)
group by dates.DT, tbl1.Something, tbl1.Customer;

1 个答案:

答案 0 :(得分:2)

您可以将分析平均值与window子句一起使用:

select dt, customer, filecount,
  avg(filecount) over (partition by customer order by dt
    rows between 3 preceding and 1 preceding) as avgfilecount
from tmp_foo
order by dt desc;

DT         CUSTOMER  FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x                10   4.66666667
2019-04-29 x                 2            6
2019-04-28 x                 3            9
2019-04-27 x                 9             

,然后使用merge语句进行更新:

merge into tmp_foo t
using (
  select dt, customer,
    avg(filecount) over (partition by customer order by dt
      rows between 3 preceding and 1 preceding) as avgfilecount
  from tmp_foo
) s
on (s.dt = t.dt and s.customer = t.customer)
when matched then update set t.avgfilecount = s.avgfilecount;

4 rows merged.

select dt, customer, filecount, avgfilecount
from tmp_foo
order by dt desc;

DT         CUSTOMER  FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x                10   4.66666667
2019-04-29 x                 2            6
2019-04-28 x                 3            9
2019-04-27 x                 9             

您没有显示原始的插入语句;可以向其中添加分析计算,并避免单独的更新步骤。

此外,如果您希望前两个日期值的计算方式类似于前两个“缺失”天数为零,则可以使用sum和除法来代替avg

select dt, customer, filecount,
  sum(filecount) over (partition by customer order by dt
    rows between 3 preceding and 1 preceding)/3 as avgfilecount
from tmp_foo
order by dt desc;

DT         CUSTOMER  FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x                10   4.66666667
2019-04-29 x                 2            4
2019-04-28 x                 3            3
2019-04-27 x                 9             

这取决于您期望这些最后计算出的值是什么。