【问题标题】:Finding first of consecutive dates using analytical functions使用分析函数查找第一个连续日期
【发布时间】:2016-06-29 00:55:10
【问题描述】:

我需要分析存储在一个 Oracle 数据库表中的一段数据。此表中的示例行如下所示:

Department  Date    Status
------------------------------------
D1          2016/6/1    1
D1          2016/5/31   0
D1          2016/5/30   0
D1          2016/5/29   1
D1          2016/5/28   0
D1          2016/5/27   1
D2          2016/6/1    0
D2          2016/5/31   1
D2          2016/5/30   1
D2          2016/5/29   0
D2          2016/5/28   0
D2          2016/5/27   1

每个部门和每个日期都有一行。我想要的是,对于任何给定日期的每个部门,找到Status 在此日期之前为 0 的第一个日期。例如,对于部门 D1,对于 2016/6/1,结果是 2015/5/30,对于 D1、2016/5/31 和 5/30,结果也是 2015/5/30。对于 D1,2016/5/29 和 5/28,它是 2016/5/28。

我需要将结果输出为SELECT 结果并将其存储在另一个表中。因此结果表的行数与原始表完全相同。结果表中大概有三列:DepartmentDateTargetDate

我对 SQL / PL SQL 的经验很少。我想我应该使用分析函数和窗口,但我真的无法提出查询。请给我一些建议。谢谢!

【问题讨论】:

  • 分析功能是正确的(和窗口),但首先请澄清要求。 D2 和 2016/5/27 的预期结果是什么?也就是说,如果一个部门的最旧行的状态为 1(或更一般地说,不为零)怎么办? ....实际上等等,你说当状态为 0 但在你的例子中 D1 和日期 5/29 和 5/28 你说结果应该是 5/27,但 5/27 的状态是 1,而不是 0 。 请说清楚。 (最好通过编辑您的帖子,而不是在 cmets 中。)
  • @mathguy 感谢您的评论。 1) 如果最旧的行的状态为 1,或者与零不同,则 TargetDate 应该为空,这意味着我们找不到该日期之前状态为 0 的任何日期。2) 抱歉。对于 D1 和 5/29、5/28,结果应该是 2016/5/28。那是一个错字。我已经在原帖中更正了。
  • 酷,谢谢。再问一个问题,以确保我理解要求。似乎对于每个日期,您都会转到状态为零的最近日期(如果状态为零,则可能是日期本身,或者返回历史记录直到找到第一个零)。然后不要选择状态为零的最新日期;相反,如果它是状态为零的连续日期序列的一部分,请从该连续序列中选择最旧的日期。这就是为什么 6/1、5/31 和 5/30 的答案是 D1 部门的 5/30。正确的?如果是这样,这就是所谓的“tabibitosan”方法的应用。
  • 您理解的@mathguy 是绝对正确的(查找状态全部为0的连续日期块的第一个/最旧日期。并且该块本身已关闭并且在给定日期之前。状态在给定的日期可以是 1 或 0) 。由于您已经清楚地命名了该方法(并且我在 Oracle 社区上找到了该文章),因此我将尝试自己理解这一点。如果我仍然无法弄清楚,我会回到你身边。非常感谢!
  • 我错了; tabibitosan 方法在这种情况下无济于事,但分析函数lag() 会(除了我想到的其他任何东西)。在下面发布它作为答案。

标签: oracle plsql data-analysis


【解决方案1】:
with test_data (department, date_, status) as (
       select 'D1', date '2016-06-01', 1 from dual union all
       select 'D1', date '2016-05-31', 0 from dual union all
       select 'D1', date '2016-05-30', 0 from dual union all
       select 'D1', date '2016-05-29', 1 from dual union all
       select 'D1', date '2016-05-28', 0 from dual union all
       select 'D1', date '2016-05-27', 1 from dual union all
       select 'D2', date '2016-06-01', 0 from dual union all
       select 'D2', date '2016-05-31', 1 from dual union all
       select 'D2', date '2016-05-30', 1 from dual union all
       select 'D2', date '2016-05-29', 0 from dual union all
       select 'D2', date '2016-05-28', 0 from dual union all
       select 'D2', date '2016-05-27', 1 from dual
     ),
     t (department, date_, status, lagged_status) as (
       select department, date_, status,
              lag(status) over (partition by department order by date_)
       from   test_data
     ) 
select   department, date_, 
         max(case when status = 0 and (lagged_status = 1 or lagged_status is null) 
                  then date_ end)
             over (partition by department order by date_
                   rows between unbounded preceding and current row) as target_date
from     t
order by department, date_ desc
;

结果:

DEPARTMENT  DATE_      TARGET_DATE
----------- ---------- -----------
D1          2016-06-01 2016-05-30
D1          2016-05-31 2016-05-30
D1          2016-05-30 2016-05-30
D1          2016-05-29 2016-05-28
D1          2016-05-28 2016-05-28
D1          2016-05-27 (null)
D2          2016-06-01 2016-06-01
D2          2016-05-31 2016-05-28
D2          2016-05-30 2016-05-28
D2          2016-05-29 2016-05-28
D2          2016-05-28 2016-05-28
D2          2016-05-27 (null)

12 rows selected.

请注意,“日期”是 Oracle 关键字,不应用作列名;我使用 date_ 代替(我添加了一个下划线)。

【讨论】:

  • 我相信这是正确的答案。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-17
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 1970-01-01
  • 2021-02-28
相关资源
最近更新 更多