SQL-Oracle 使用 LAG() 从自身更新表
Posted
技术标签:
【中文标题】SQL-Oracle 使用 LAG() 从自身更新表【英文标题】:SQL-Oracle Update table from itself using LAG() 【发布时间】:2017-09-15 09:07:00 【问题描述】:假设我们有一张这样的表格:
| Year | Month | Day | ID_Office | Operations | Operations yesterday |
|------|-------|-----|-----------|------------|----------------------|
| 2016 | 12 | 31 | 9555 | 500 | 0 |
| 2017 | 1 | 1 | 9555 | 600 | 0 |
| 2017 | 1 | 2 | 9555 | 750 | 0 |
| 2017 | 1 | 3 | 9556 | 800 | 0 |
我想使用上一行中的“操作”更新“昨天的操作”中的值。所以结果表一定是这样的:
| Year | Month | Day | ID_Office | Operations | Operations yesterday |
|------|-------|-----|-----------|------------|----------------------|
| 2016 | 12 | 31 | 9555 | 500 | 0 |
| 2017 | 1 | 1 | 9555 | 600 | 500 |
| 2017 | 1 | 2 | 9555 | 750 | 600 |
| 2017 | 1 | 3 | 9556 | 800 | 0 |
我将以下代码与函数 LAG() 一起使用,但它没有更新正确的值。
Update table1 F1
Set f1.operations_yesterday =
(Select LAG(f1.operations, 1, 0) OVER(ORDER BY year, month, day)
From table1 F2
Where
F1.year= F2.year And
F1.month= F2.month And
F1.day= F2.day);
你能从我的代码中给我一些关于到底哪里错的建议吗? 我在“Insert Into”语句上使用 lag() 函数取得了成功,但在这种情况下,我必须提供一个 Update 语句解决方案。
注意: 请注意,排序的字段可能超过三个。我仅以年、月和日为例,但还有更多与日期无关的字段。 此外,重要的是要详细说明并非每天都有价值。
提前谢谢你!
【问题讨论】:
我自己没有使用过LAG()
,但我想知道您是否不应该在以下脚本中使用f2
,而不是f1
:...Select LAG(f1.operations, 1, 0)...
跨度>
@PeterAbolins 确实,这是最初尝试过的,但它仍然无法正常工作。
【参考方案1】:
不建议冗余存储数据。不过……
您想在记录中存储前一天的数据。您假设每天都有一条记录,因此 day before 也将是按日期排序的 record before。但是这些知识并没有真正的帮助,因为选择前一天比选择前一天的记录要容易得多。
将日、月和年分开存储而不仅仅是日期,这很奇怪。我不知道你想用这个实现什么。我们必须笨拙地转换它。
update table1 today
set operations_yesterday =
(
select operations
from table1 yesterday
where to_date(yesterday.year * 10000 + yesterday.month * 100 + yesterday.day, 'yyyymmdd')
= to_date(today.year * 10000 + today.month * 100 + today.day, 'yyyymmdd') - 1
);
如果您存储日期,那将是简单的:
update table1 today
set operations_yesterday =
(select operations from table1 yesterday where yesterday.date = today.date - 1);
添加COALESCE
(或Oracle 的NVL
),如果你想要0 而不是昨天没有的null。
【讨论】:
感谢托尔斯滕·凯特纳。但是,我错过了一些可能会改变您解决问题的方式的解释。我只放了三个字段(年、月、日),但还有更多,其中一些与日期无关。此外,您的第二个解决方案不是一个选项,因为并非每一天都有价值。 @Roy90:其他专栏将如何发挥作用?您要更新的列不止一列吗? “并非每一天都有价值”是什么意思?您分隔列是因为值可以为空,例如“2016 年 11 月”没有一天,或者“每年 2 月 10 日”,无论哪一年? 如前所述,无论如何您都不应该这样做。选择前一天的记录非常容易。如果将这些值存储在当天的记录中,则会冗余存储数据,这可能会导致不一致。 我已经编辑了我的初始帖子,增加了一个字段。关于“并非每一天都有值”,我的意思是可能是一些随机的日子我没有在表中插入值,如果您尝试访问这个具体日期,将没有数据。最后,我并没有完全存储前一天的值,我希望这个值从当前值中减去,然后存储结果。 关于附加栏ID_Office
:我们不做任何事情;它保持原样。应该是这样。至于错过的日子:然后我们用 null 更新。应该是这样。至于存储结果:仍然冗余存储数据。您总是可以从另一个值中减去一个值。如果您存储这些值,您可能会在某一天由于某些错误找到前一天 = 100,当天 = 200,存储结果 = 50。那该怎么办?哪个值是正确的?最好不要存储结果。如图所示,选择前一天非常简单。【参考方案2】:
只要您在子查询中传递年、月和日的值,就只有一行可用于延迟函数,它总是会给您一个空值。正确的方式应该是:
UPDATE table1 f1
SET f1.operations_yesterday =
(WITH table1_lag AS (SELECT ff.YEAR,
ff.MONTH,
ff.DAY,
lag(ff.operations, 1, 0) over(ORDER BY ff.YEAR, ff.MONTH, ff.DAY) AS yesterday
FROM table1 ff)
SELECT f2.yesterday
FROM table1_lag f2
WHERE f1.year = f2.year
AND f1.month = f2.month
AND f1.day = f2.day);
【讨论】:
感谢 San,我仍在修改您的解决方案并进行尝试。有事我会第一时间回复你的。 我在 ORDER BY 的末尾和最后的 WHERE 处添加了“ID_Office”,但结果不正确。它应该从同一个 ID_Office 获取前一行,但现在它获取前一行忽略 ID_OFFICE。有没有办法可以使用您的代码按 ID_OFFICE 分组?因为,除了这个字段的添加,它似乎工作正常。【参考方案3】:如果缺少几天,并且您想要前天的值,那么您可以使用merge
:
merge into table1 t1
using (select lag(operations, 1, 0)
over (partition by id_office order by year, month, day) lop
from table1 t) t2
on (t1.rowid = t2.rowid)
when matched then update set operations_yesterday = t2.lop;
测试数据:
create table table1(Year number(4), Month number(2), Day number(2),
ID_Office number(5), Operations number(4), Operations_yesterday number(4));
insert into table1 values (2016, 12, 31, 9555, 500, null);
insert into table1 values (2017, 1, 1, 9555, 600, null);
insert into table1 values (2017, 1, 2, 9555, 750, null);
insert into table1 values (2017, 1, 3, 9556, 800, null);
insert into table1 values (2017, 1, 5, 9556, 400, null);
...在merge
之后:
select * from table1;
YEAR MONTH DAY ID_OFFICE OPERATIONS OPERATIONS_YESTERDAY
----- ----- --- --------- ---------- --------------------
2016 12 31 9555 500 0
2017 1 1 9555 600 500
2017 1 2 9555 750 600
2017 1 3 9556 800 0
2017 1 5 9556 400 800
【讨论】:
以上是关于SQL-Oracle 使用 LAG() 从自身更新表的主要内容,如果未能解决你的问题,请参考以下文章
将值提高到 n 次方时 SQL-Oracle 代码出错 - 需要帮助
基于其他两列并使用 LAG 函数更新 Oracle 过程中的列