Oracle SQL 查询将某行的数据与 28 天前的数据进行比较
Posted
技术标签:
【中文标题】Oracle SQL 查询将某行的数据与 28 天前的数据进行比较【英文标题】:Oracle SQL query to compare data of a row with that of 28 days back 【发布时间】:2016-08-17 14:54:29 【问题描述】:我有一个如下表 TableA:
WEEK |COL1 |COL2 |COL3 |COL4 |COL5 |CLOSING_BALANCE |REPORT_DATE
-----|-----|-----|-----|-----|-----|----------------|-----------
----------------------------------------------------------------
WEEK_1|123|Y|1|123|Y|100|19/07/2016
WEEK_5|123|Y|1|123|Y|300|16/08/2016
WEEK_9|123|Y|1|123|Y|400|13/09/2016
WEEK_5|345|N|2|859|N|658|16/08/2016
WEEK_1|456|N|5|795|N|50|19/07/2016
WEEK_13|456|N|5|795|N|230|11/10/2016
WEEK_9|456|N|5|795|N|120|13/09/2016
WEEK_1|567|Y|4|567|N|111|19/07/2016
WEEK_13|567|Y|4|567|N|222|11/10/2016
WEEK_1|678|N|2|564|Y|900|19/07/2016
WEEK_9|789|N|3|458|Y|200|13/09/2016
每一行都需要与 COL1、COL2、COL3、COL4 和 COL5 的相同组合的 28 天 (report_date-28) 回溯数据进行比较。
假设我将 week_5 与 week_1 进行比较
案例 1:出现在当前行,也出现在 28 天前。
假设第 5 周,我在 28 天前(即第 1 周)得到了 COL1、COL2、COL3、COL4 和 COL5 的匹配组合。现在在输出中,我想要这两行的 1 行,其中 week_1 closing_base 作为 prev_closing_base 和 week_5 closing_base 作为 current_closing_base。
例如:week_5/123 week_1/123
案例 2:28 天前出现,但当前缺失
如果一个组合出现在第 1 周但在第 5 周丢失。我希望 prev_closing_base 作为 week_1 的 closing_base 和 current_closing_base 作为 0。 示例:week_1/678
案例 3:存在于当前行,但在 28 天前丢失了
如果一个组合出现在第 5 周但在第 1 周丢失。我希望 prev_closing_base 为 0,current_closing_base 为第 5 周的 close_base。 示例:week5/345 & week_9/789
输出应该如下:-
WEEK | COL1 | COL2 | COL3 | COL4 | COL5 | CLOSING_BALANCE_28DAYS_AGO | CURR_CLOSING_BALANCE
----------------------------------------------------------------------------------------------
WEEK_1 |456|N|5|795|N|50 |0
WEEK_5 |123|Y|1|123|Y|100|300
WEEK_5 |345|N|2|859|N|0 |658
WEEK_1 |678|N|2|564|Y|900|0
WEEK_9 |789|N|3|458|Y|0 |200
WEEK_9 |123|Y|1|123|Y|300|400
WEEK_13|456|N|5|795|N|120|230
WEEK_1 |567|Y|4|567|N|111|0
WEEK_13|567|Y|4|567|N|0 |222
我尝试在同一张表上进行完全外连接。但这不起作用。它给出了重复的行。
select nvl (curr.WEEK, prev.WEEK) WEEK,
nvl (curr.COL1, prev.COL1) COL1,
nvl (curr.COL2, prev.COL2) COL2,
nvl (curr.COL3, prev.COL3) COL3,
nvl (curr.COL4, prev.COL4) COL4,
nvl (curr.COL5, prev.COL5) COL5,
nvl (prev.CLOSING_BALANCE, 0) PREV_CLOSING_BALANCE_28DAYS_AGO,
nvl (curr.CLOSING_BALANCE, 0) CURR_CLOSING_BALANCE
from TableA curr
full outer join TableA prev
on curr.report_date - 28 = prev.report_date
and curr.COL1 = prev.COL1
and curr.COL3 = prev.COL3
and curr.COL4 = prev.COL4
and curr.COL2 = prev.COL2
and curr.COL5 = prev.COL5;
左外连接似乎也不起作用
select nvl (curr.year_week, prev.year_week) year_week,
nvl (curr.COL1, prev.COL1) COL1,
nvl (curr.COL2, prev.COL2) COL2,
nvl (curr.COL3, prev.COL3) COL3,
nvl (curr.COL4, prev.COL4) COL4,
nvl (curr.COL5, prev.COL5) COL5,
nvl (prev.CLOSING_BALANCE, 0) PREV_CLOSING_BALANCE_28DAYS_AGO,
nvl (curr.CLOSING_BALANCE, 0) CURR_CLOSING_BALANCE
from TableA curr
left outer join TableA prev
on curr.edw_report_date - 28 = prev.edw_report_date
and curr.COL1 = prev.COL1
and curr.COL3 = prev.COL3
and curr.COL4 = prev.COL4
and curr.COL2 = prev.COL2
and curr.COL5 = prev.COL5;
左外连接的输出:-
YEAR_WEEK|MOB_PROMOTION_KEY|BTM_EMPLOYEE_FLG|SIM_ACTIVATION_STATUS_KEY|BTM_HANDSET_KEY|BTM_ADDON_XSPEED4G_FLG|CLOSING_BASE_28DAYS_AGO|CURR_CLOSING_BASE
------------------------------------------------------------------------------------------------------------------------------------------------------------
WEEK_5|123|Y|1|123|Y|100|300
WEEK_1|123|Y|1|123|Y|0|100
WEEK_1|678|N|2|564|Y|0|900
WEEK_5|345|N|2|859|N|0|658
WEEK_9|789|N|3|458|Y|0|200
我发现遵循如下程序方法会给我想要的输出。但我在想是否可以使用单个查询来完成。需要您对此提出宝贵建议。
BEGIN
for i in (select distinct a.report_date curr_date, b.report_date prev_date from TableA a, TableA b where a.report_date-28=b.report_date order by a.report_date)
LOOP
insert into TARGET_TABLE
select nvl (curr.WEEK, prev.WEEK) WEEK,
nvl (curr.COL1, prev.COL1) COL1,
nvl (curr.COL2, prev.COL2) COL2,
nvl (curr.COL3, prev.COL3) COL3,
nvl (curr.COL4, prev.COL4) COL4,
nvl (curr.COL5, prev.COL5) COL5,
nvl (prev.CLOSING_BALANCE, 0) CLOSING_BALANCE_28DAYS_AGO,
nvl (curr.CLOSING_BALANCE, 0) CURR_CLOSING_BALANCE
from TableA curr
inner join TableA prev
on curr.report_date-28= prev.report_date
and curr.COL1 = prev.COL1
and curr.COL3 = prev.COL3
and curr.COL4 = prev.COL4
and curr.COL2 = prev.COL2
and curr.COL5 = prev.COL5
where curr.report_date = i.curr_date
and prev.report_date = i.prev_date;
commit;
insert into TARGET_TABLE
select nvl (curr.WEEK, prev.WEEK) WEEK,
nvl (curr.COL1, prev.COL1) COL1,
nvl (curr.COL2, prev.COL2) COL2,
nvl (curr.COL3, prev.COL3) COL3,
nvl (curr.COL4, prev.COL4) COL4,
nvl (curr.COL5, prev.COL5) COL5,
nvl (prev.CLOSING_BALANCE, 0) CLOSING_BALANCE_28DAYS_AGO,
nvl (curr.CLOSING_BALANCE, 0) CURR_CLOSING_BALANCE
from TableA curr
left outer join TableA prev
on curr.report_date-28= prev.report_date
and curr.COL1 = prev.COL1
and curr.COL3 = prev.COL3
and curr.COL4 = prev.COL4
and curr.COL2 = prev.COL2
and curr.COL5 = prev.COL5
where curr.report_date = i.curr_date
and not exists (select 1 from TARGET_TABLE tmp
where tmp.COL1 = curr.COL1
and tmp.COL3 = curr.COL3
and tmp.COL4 = curr.COL4
and tmp.COL2 = curr.COL2
and tmp.COL5 = curr.COL5) ;
commit;
insert into TARGET_TABLE
select nvl (curr.WEEK, prev.WEEK) WEEK,
nvl (curr.COL1, prev.COL1) COL1,
nvl (curr.COL2, prev.COL2) COL2,
nvl (curr.COL3, prev.COL3) COL3,
nvl (curr.COL4, prev.COL4) COL4,
nvl (curr.COL5, prev.COL5) COL5,
nvl (prev.CLOSING_BALANCE, 0) CLOSING_BALANCE_28DAYS_AGO,
nvl (curr.CLOSING_BALANCE, 0) CURR_CLOSING_BALANCE
from TableA curr
right outer join TableA prev
on curr.report_date-28= prev.report_date
and curr.COL1 = prev.COL1
and curr.COL3 = prev.COL3
and curr.COL4 = prev.COL4
and curr.COL2 = prev.COL2
and curr.COL5 = prev.COL5
where prev.report_date = i.prev_date
and not exists (select 1 from TARGET_TABLE tmp
where tmp.COL1 = prev.COL1
and tmp.COL3 = prev.COL3
and tmp.COL4 = prev.COL4
and tmp.COL2 = prev.COL2
and tmp.COL5 = prev.COL5) ;
commit;
end loop;
end;
/
【问题讨论】:
你试过用left outer join
代替full outer join
吗?
@Vercelli :是的,也试过了。但是左外连接也没有给出预期的结果。
使用左外连接得到 5 行而不是 9 行。为什么您的预期结果只有四行 - 为什么不包括 week_1/123
?
28 天前总会有记录吗? LEFT OUTER SELF JOIN 应该(99% 的信心)是您想要的,所以如果您没有得到您想要的结果,也许您可以将您的尝试发布在左连接处,我们可以帮助您发现潜在的问题加入。请记住,当使用 LEFT、RIGHT 或 FULL 外连接时,不要在 where 子句中使用外部表,否则它是内连接,除非您只是测试 null
你的逻辑不是很清楚。 WEEK_1/123 是否没有行,因为该键 WEEK_5/123 的后一行优先;所以你想要一个键的最新行,包含 28 天前的数据?我几乎可以完成这项工作,但为什么你的 WEEK_1 值是 900,0 而不是 0,900 - 该键没有前一行?
【参考方案1】:
那么,这会在给定数据上给出所需的结果:
SELECT nvl (curr.week, prev.week) year_week,
nvl (curr.COL1, prev.COL1) COL1,
nvl (curr.COL2, prev.COL2) COL2,
nvl (curr.COL3, prev.COL3) COL3,
nvl (curr.COL4, prev.COL4) COL4,
nvl (curr.COL5, prev.COL5) COL5,
nvl (prev.CLOSING_BALANCE, 0) PREV_CLOSING_BALANCE_28DAYS_AGO,
nvl (curr.CLOSING_BALANCE, 0) CURR_CLOSING_BALANCE
FROM TableA curr
FULL OUTER JOIN TableA prev
ON curr.REPORT_DATE - 28 = prev.report_date
AND curr.COL1 = prev.COL1
AND curr.COL3 = prev.COL3
AND curr.COL4 = prev.COL4
AND curr.COL2 = prev.COL2
AND curr.COL5 = prev.COL5
JOIN (
SELECT DISTINCT a.week prev_week, b.week curr_week, a.report_date prev_date, b.report_date curr_date
FROM TableA a
JOIN TableA b ON a.report_date+28=b.report_date
ORDER BY a.report_date
) AS dates
ON (curr.week = dates.curr_week AND curr.REPORT_DATE = dates.curr_date)
OR (prev.week = dates.prev_week AND prev.REPORT_DATE = dates.prev_date AND curr.REPORT_DATE IS NULL)
WHERE curr.report_date IS NOT NULL OR NOT EXISTS (
SELECT a.week
FROM TableA a
JOIN TableA b ON a.report_date+28=b.report_date
WHERE b.week = prev.week AND b.report_date = prev.report_date
)
ORDER BY curr.col1;
但代码相当复杂,所以请在更大的数据集上运行一些测试。
【讨论】:
左连接没有给我所需的结果。 你得到什么结果?我根据您提供的数据进行了测试,结果对我有用。 我不希望该行出现在输出 WEEK_1|123|Y|1|123|Y|0|100 中。由于找到了 week_5/123 和 week_1/123 的匹配项,我需要一个带有 week_5/123 的单行,因为我希望 prev_closing_base 从 week_1 的 closing_base 和 current_closing_base 填充 week_5 的 closing_base 也适用于week_1/678... 28 天前出现但当前丢失的情况。左连接给出:WEEK_1 |第678章N | 2 |第564章是 | 0 | 900 但我想要:WEEK_1 |第678章N | 2 |第564章是 | 900 | 0 好的,我知道你要去哪里了。我只有一个问题 - 你如何决定 WEEK_5 | 345 应该出现在 CURR 还是 PREV 中?它既不是最旧的也不是最新的一周,也没有更旧或更晚的值。 如果我们返回 28 天检查 week_5,我们得到 week_1。因此,来自 week_5 的行成为我的 CURR,来自 week_1 的相应行(组合匹配)成为我的 PREV。现在对于 week_5/345 我们在 week_1 中没有得到组合匹配,因此对于这一行 current_closing_base 来自 week_5/345 而 prev_closing_base 变为 0以上是关于Oracle SQL 查询将某行的数据与 28 天前的数据进行比较的主要内容,如果未能解决你的问题,请参考以下文章