有什么方法可以使此查询循环或更有效?

时间:2019-01-24 16:19:37

标签: sql oracle

我有一个员工历史记录表,其中包含状态日期,状态和员工ID。状态日期不是相同的日期,有时可能要持续几个月才能更新。我需要创建一个报告,以每月创建雇员状态的每月快照。

当前,我选择的是sysdate月份开始之前的最大状态日期,并通过一次从查询中减去一个月来重复此查询,然后进行合并以合并所有日期。

SELECT id, 
       status_date, 
       status, 
       (trunc(sysdate, 'month')) AS Activity_Month
FROM empl_hist as e1 join
    (SELECT id, max(status_date) AS max_date, status_date FROM empl_hist 
     WHERE status_date <= (trunc(sysdate, 'month')) e2 
        on e1.id = e2.id and e1.status_date = e2.status_date

UNION ALL

SELECT id, 
    status_date, 
    status,
    (trunc(add_months(sysdate,-1), 'month')) AS Activity_Month,
FROM empl_hist as e1 join
    (SELECT id, max(status_date) AS max_date, status_date FROM empl_hist 
     WHERE status_date <= (trunc(add_months(sysdate,-1), 'month')) e2 
        on e1.id = e2.id and e1.status_date = e2.status_date

我需要过去24个月的这份报告,而且似乎应该有比23个联盟更好的方法。

3 个答案:

答案 0 :(得分:1)

您可以使用递归查询来执行此操作以生成报告月份,然后将其加入到empl_hist表中并使用聚合函数来获得所需的结果:

WITH REPORT(ACTIVITY_MONTH) AS (
   SELECT CAST(ADD_MONTHS(TRUNC(SYSDATE,'month'),-24) AS DATE)
     FROM DUAL
  UNION ALL
   SELECT CAST(ADD_MONTHS(ACTIVITY_MONTH,1) AS DATE)
     FROM REPORT
    WHERE ADD_MONTHS(ACTIVITY_MONTH,1) < SYSDATE
)
SELECT eh.id
     , max(eh.status_date) status_Date
     , max(eh.status) keep (dense_rank first order by status_date desc) status
     , r.activity_month
  FROM REPORT r
  JOIN EMPL_HIST EH
    ON EH.STATUS_DATE <= r.ACTIVITY_MONTH
 group by eh.id, r.activity_month

答案 1 :(得分:1)

如果在第一步中计算有效期,即最后一天(包括该天),则可以使与 calendar 表(包含24个报告月的表)的联接更有效。状态为有效-status_date_to

如果status_date被截断(即没有时间),则建议的计算有效。

如果有时间因素,请使用status_date - interval '1' second代替status_date - 1

比起您,您只能将{em>匹配中与reporing_month相匹配的EPML_HISTORY

中的那些记录联接起来
c.reporting_month between h.status_date and h.status_date_to

另外请注意,您应该重新查看使用WITH calendar AS ( SELECT trunc(add_months(sysdate, 1-level), 'MM') reporting_month FROM dual CONNECT BY level <= 24 ), hist as ( SELECT id, status_date, nvl(lead(status_date-1) over (partition by id order by status_date),add_months(trunc(sysdate, 'MM'),1)) as status_date_to, status from empl_hist) select REPORTING_MONTH, ID, STATUS from calendar c join hist h on c.reporting_month between h.status_date and h.status_date_to order by id, reporting_month; REPORTING_MONTH ID STATUS ------------------- ---------- ---------- 01.05.2018 00:00:00 1 active 01.06.2018 00:00:00 1 active 01.07.2018 00:00:00 1 active 01.08.2018 00:00:00 1 active 01.09.2018 00:00:00 1 deactive 01.10.2018 00:00:00 1 deactive 01.11.2018 00:00:00 1 deactive 01.12.2018 00:00:00 1 deactive 01.01.2019 00:00:00 1 active 01.08.2018 00:00:00 2 active 01.09.2018 00:00:00 2 active 01.10.2018 00:00:00 2 active 01.11.2018 00:00:00 2 deactive 01.12.2018 00:00:00 2 deactive 01.01.2019 00:00:00 2 deactive 01.01.2019 00:00:00 4 active 指定报告月份的逻辑。在status_date <= (trunc(sysdate, 'month')下面的示例数据中,由于id=3在当月5号被激活,因此被忽略,但是报告id=4是因为它在当月1号处于活动状态。

样本数据

drop table empl_hist;
create table empl_hist(
id number,
status_date date,
status varchar2(10));

insert into empl_hist values(1,DATE'2018-05-01','active');
insert into empl_hist values(1,DATE'2018-08-05','deactive');
insert into empl_hist values(1,DATE'2018-12-05','active');
insert into empl_hist values(2,DATE'2018-07-05','active');
insert into empl_hist values(2,DATE'2018-10-05','deactive');
insert into empl_hist values(3,DATE'2019-01-05','active');
insert into empl_hist values(4,DATE'2019-01-01','active');

答案 2 :(得分:0)

您可以使用子查询生成24个月的列表并将其加入。然后,我将使用row_number来确定窗口中最近的一条记录,以避免发生自我联接:

SELECT   id, status_date, status, Activity_Month
FROM     (
            SELECT     e.*,
                       m.Activity_Month,
                       row_number() OVER (PARTITION BY m.Activity_Month 
                                          ORDER BY e.status_date DESC) rn
            FROM       (
                          SELECT  trunc(add_months(sysdate, 1-level), 'month') Activity_Month
                          FROM    dual
                          CONNECT BY level <= 24
                       ) m
            INNER JOIN empl_hist e
                    ON e.status_date <= m.Activity_Month
         ) base
WHERE    rn = 1
ORDER BY Activity_Month