Oracle:使用来自同一表的聚合值更新表中的值

Posted

技术标签:

【中文标题】Oracle:使用来自同一表的聚合值更新表中的值【英文标题】:Oracle: Update values in table with aggregated values from same table 【发布时间】:2019-05-09 09:19:18 【问题描述】:

我正在寻找一种可能更好的方法来解决这个问题。

我在 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 字段中没有空白,但显然有第一个我找不到任何以前的记录。如果我要循环,我可以很容易地用(the record before previous record/2 + previous record)/3 计算AvgFilecount,而我不能用UPDATE 计算,因为我不能保证它们的执行顺序。所以我可以只取最后 3 条记录(通过 DT)并从那里计算。

我认为很容易更新的内容让我很头疼。我主要在做 SQL Server,我只会加入其他 3 条记录,但在 Oracle 中似乎有点不同。我找到了https://***.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 - “不能修改映射到非键保留表的列” *原因:试图插入或更新连接视图的列 映射到非键保留表。 *Action:直接修改底层基表。

我认为这应该可以工作,因为两个连接条件都在主键中,因此是唯一的。我目前正在实施上述答案中的第一种方法,但它变得相当大,感觉应该有更好的解决方案。

我想过尝试的其他事情:

使用嵌套子选择(嵌套是因为 Oracle 不知道 top(n),我需要对子选择进行排序)选择由 DT​​ 排序的前 3 条记录,然后使用 rownum AVG()。但是,有人告诉我 subselect 可能会很慢,并且在 Oracle 性能方面连接更好。不知道是不是真的这样,还没有做任何测试

编辑:我的插入现在看起来像这样。我已经汇总了一天的 Filecount,因为每个 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】:

您可以使用带有窗口子句的分析平均值:

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 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             

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

【讨论】:

很抱歉回答迟了。我在帖子中添加了我的插入(最后作为编辑)。我想我想我已经有其他问题了,所以我把它分成两部分,但这完全没有必要。

以上是关于Oracle:使用来自同一表的聚合值更新表中的值的主要内容,如果未能解决你的问题,请参考以下文章

Oracle:Sql根据同一表另一行的值更新同一表的行

用 Oracle 中同一张表中的其他行数据更新一行

SQL 更新一个表中的值以匹配 Oracle SQL Developer 中另一个表的值

在更新触发器之前或之后更改同一表中的值(oracle)

Oracle:使用其他表中的值进行更新

SQL中同一个表中,一个字段里面的值等于 另外一个表里面的值 例如表ABC 字段D里面的值如何更新到字段E里面