可变日期范围内的总金额

Sum Amounts That Fall Within Variable Date Ranges

我有两个 table 类似于下面的示例 table。 PAY table 显示了员工的薪水和时间。 ENROLL table 显示员工何时注册 'E' 以及他们后来何时终止 'T'。因此,在这种情况下,员工从 3/1/14 注册到 5/31/14,然后从 10/01/14 注册到 11/30/14

支付 table

EMPLID      PAY_END_DT    PAY_AMT
00100001    31-JAN-14     110
00100001    28-FEB-14     120
00100001    31-MAR-14     130     <-- should be included in SUM
00100001    30-APR-14     140     <-- should be included in SUM
00100001    31-MAY-14     150     <-- should be included in SUM
00100001    30-JUN-14     160
00100001    31-JUL-14     170
00100001    31-AUG-14     180
00100001    30-SEP-14     190
00100001    31-OCT-14     200     <-- should be included in SUM
00100001    30-NOV-14     210     <-- should be included in SUM
00100001    31-DEC-14     220

注册table

EMPLID      EFFDT       STATUS
00100001    01-MAR-14   E
00100001    31-MAY-14   T
00100001    01-OCT-14   E
00100001    30-NOV-14   T

我想要的是一个 SQL,它将对 PAY_AMT 列求和,但仅适用于在员工注册时发生的 PAY_END_DTPAY_AMT .例如,在这种情况下,我只想将 PAY_AMTPAY_END_DT 归入 3/1/145/31/1410/01/1411/30/14。所以,正确的结果是:

EMPLID    SUM
00100001  830

我需要 运行 这个 SQL 用于很多不同的 EMPLIDs,其中一些可能有两个注册期,就像这个例子,而其他人可能有零、一、三个或多个注册期。

我曾考虑过使用 lead(),但不太清楚在这种情况下如何使用它。我也考虑过使用 between...and... 但由于注册期的数量可能会有所不同,我不知道如何处理。

我不想使用 PL/SQL。

为了让事情变得更简单,下面是这两个 table 的模板:

WITH 
PAY AS(
SELECT '00100001' AS EMPLID, TO_DATE('2014-01-31', 'YYYY-MM-DD') AS PAY_END_DT, 110.00 AS PAY_AMT FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-02-28', 'YYYY-MM-DD'), 120.00 AS PAY_AMT FROM DUAL
UNION ALL
--SUM column would include this PAY_AMT
SELECT '00100001', TO_DATE('2014-03-31', 'YYYY-MM-DD'), 130.00 AS PAY_AMT FROM DUAL
UNION ALL
--SUM column would include this PAY_AMT
SELECT '00100001', TO_DATE('2014-04-30', 'YYYY-MM-DD'), 140.00 AS PAY_AMT FROM DUAL
UNION ALL
--SUM column would include this PAY_AMT
SELECT '00100001', TO_DATE('2014-05-31', 'YYYY-MM-DD'), 150.00 AS PAY_AMT FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-06-30', 'YYYY-MM-DD'), 160.00 AS PAY_AMT FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-07-31', 'YYYY-MM-DD'), 170.00 AS PAY_AMT FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-08-31', 'YYYY-MM-DD'), 180.00 AS PAY_AMT FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-09-30', 'YYYY-MM-DD'), 190.00 AS PAY_AMT FROM DUAL
UNION ALL
--SUM column would include this PAY_AMT
SELECT '00100001', TO_DATE('2014-10-31', 'YYYY-MM-DD'), 200.00 AS PAY_AMT FROM DUAL
UNION ALL
--SUM column would include this PAY_AMT
SELECT '00100001', TO_DATE('2014-11-30', 'YYYY-MM-DD'), 210.00 AS PAY_AMT FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-12-31', 'YYYY-MM-DD'), 220.00 AS PAY_AMT FROM DUAL
),
ENROLL AS (
SELECT '00100001' AS EMPLID, TO_DATE('2014-03-01', 'YYYY-MM-DD') AS EFFDT, 'E' AS STATUS FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-05-31', 'YYYY-MM-DD'), 'T' FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-10-01', 'YYYY-MM-DD'), 'E' FROM DUAL
UNION ALL
SELECT '00100001', TO_DATE('2014-11-30', 'YYYY-MM-DD'), 'T' FROM DUAL
)

你对lead()的猜测是正确的,解决方法也很简单:

with pay as (...),
     enroll as (...)
select pay.emplid, sum(pay_amt) 
    from pay, 
         (select emplid, effdt effdt_start, status, 
                 lead(effdt, 1) over (partition by emplid order by effdt) effdt_end
            from enroll) enr
   where enr.emplid = pay.emplid
     and enr.status = 'E'
     and pay.pay_end_dt between enr.effdt_start and enr.effdt_end
   group by pay.emplid;