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:使用来自同一表的聚合值更新表中的值的主要内容,如果未能解决你的问题,请参考以下文章