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 天前的数据进行比较的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL 中,如何找到某行的总和?

sql查询中,如何将某列 分成 两列。

Dev中gridControl控件怎么讲某行的两列单元格合并

Oracle查询语句怎么样按天分组

ORACLE nvl函数

oracle 中怎样把查询结果当做已知量或赋值给某个变量?