用于检查连续时间轴分配的SQL

时间:2018-04-20 09:58:10

标签: sql sql-server oracle

我们有2个表有多对多的关系,这些表可以简化为:

重要归因于M_Plan_ID的M-Plan表:

M_Plan_ID
VM-000001
VM-000008

授权表具有有效日期的授权ID:

GID             START_DATE      END_DATE    ACTIVE_TIME_LINE
VG-000813       21/04/2018      28/04/2019  Y
VG-000808       29/04/2019      22/04/2020  Y
VG-000800       23/04/2020      18/04/2033  Y
VG-000812       19/04/2033      31/12/9999  Y
VG-000811       22/08/2018      NULL        N

关系表描述分配给M-Plan的拨款:

M_Plan_ID       GID
VM-000001       VG-000813
VM-000001       VG-000812
VM-000008       VG-000813
VM-000008       VG-000800

业务要求是,如果为M-Plan分配了多个拨款,那么所有已分配的拨款必须在日期中连续。

我们需要编写SQL,它只会向M-Plan显示缺少的Grants间隔:

例如,已为VM-000001分配了授权VG-000813和VG-000812但缺少的间隔为VG-000808和VG-000800。

同样,VM-000008已被分配VG-000813和VG-000800螺母缺失间隔为VG-000808。

期望的结果是:

M_Plan_ID  GID
VM-000001  VG-000808
VM-000001  VG-000800
VM-000008  VG-000808

可以通过以下语句生成相同的表和数据:

create table M_Plan_ID (M_Plan_ID varchar(100)  );
insert into M_Plan_ID(M_Plan_ID) values('VM-000001');
insert into M_Plan_ID(M_Plan_ID) values('VM-000008');

create table grants (gid varchar(100), start_date date, end_date date, active varchar(1));
insert into grants values ('VG-000813',cast('2018-04-21' as Date),cast('2019-04-28' as Date),'Y');
insert into grants values ('VG-000808',cast('2019-04-29' as Date),cast('2020-04-22' as Date),'Y');
insert into grants values ('VG-000800',cast('2020-04-23' as Date),cast('2033-04-18' as Date),'Y');
insert into grants values ('VG-000812',cast('2033-04-19' as Date),cast('9999-12-31' as Date),'Y');
insert into grants values ('VG-000811',cast('2018-08-22' as Date),null,'n');

create table rel  (M_Plan_ID varchar(100), GID varchar(100) )
insert into rel values('VM-000001','VG-000813');
insert into rel values('VM-000001','VG-000812');
insert into rel values('VM-000008','VG-000813');
insert into rel values('VM-000008','VG-000800');

我正在尝试的SQL是这样的:

select
T.*,
case when start_date=lead_end_date_1 then 'True' else 'False' end
from
(
select 
M.M_Plan_ID, 
G.GID, 
G.start_date,
ISNULL(DATEADD(D, -1,LEAD(G.start_date) over (partition by M.M_Plan_ID order by g.start_date)), cast('9999-12-31' as Date)) as lead_end_date_1,
count(*) over(partition by M.M_Plan_ID) as count_partition_M_Plan_ID
from M_Plan_ID M
join rel R on M.M_Plan_ID=R.M_Plan_ID
join grants G on R.GID=G.GID
) T
where count_partition_M_Plan_ID>1
and lead_end_date_1!='9999-12-31'.

但有了这个,我只能得到连续性突破的点,我们只需要看到所有缺失的间隔。

非常感谢您在SQL中的帮助。

谢谢

2 个答案:

答案 0 :(得分:0)

下面的SQL(MS Sql Server)从可能的其他计划中筛选出活动计划 在现有的间隔内。
与目前的有效计划相比,过滤那些启动太晚或结束太早的那些。

with ACTIVEPLANS AS (
    select p.M_Plan_ID, g.GID, 
    g.[START_DATE], 
    coalesce(g.[END_DATE], cast('9999-12-31' as date)) as [END_DATE],
    min(g.[START_DATE]) over (partition by p.M_Plan_ID) as MinStartDate,
    max(coalesce(g.[END_DATE],cast('9999-12-31' as date))) over (partition by p.M_Plan_ID) as MaxEndDate
    from M_Plan_ID p
    join rel r on (p.M_Plan_ID = r.M_Plan_ID)
    join grants g on (g.GID = r.GID and g.active = 'Y')
)
select p.M_Plan_ID, g.GID
from M_Plan_ID p
join grants g on (p.M_Plan_ID <> g.GID and g.active = 'Y')
where not exists (
    select 1 
    from ACTIVEPLANS gp
    where gp.M_Plan_ID = p.M_Plan_ID
      and (
            gp.GID = g.GID
            or (gp.[START_DATE] >= g.[START_DATE] and gp.[END_DATE] <= g.[END_DATE])
            or gp.MaxEndDate <= g.[START_DATE]
            or gp.MinStartDate >= g.[END_DATE]
      )
);

答案 1 :(得分:0)

我假设赠款在这里不重叠。我的方法是首先弄清楚每个计划已经分配的拨款的日期范围的开始和结束(CTE PlanRange计算的)。

一旦我有了,我就可以使用 CROSS JOIN 来包含每个计划的指定日期范围的所有授权。这给了应该分配给每个计划的拨款。在此之后,对已经分配的授权进行 LEFT OUTER JOIN 很容易,然后只包括 rel 中没有条目的授权(即R .M_Plan_ID IS NULL)

请参阅:SQL Fiddle

WITH PlanRange AS (
    SELECT
        M.M_Plan_ID
    ,   MIN( G.start_date ) AS start_date
    ,   MAX( G.end_date ) AS end_date
    FROM
        M_Plan_ID AS M
    JOIN
        rel AS R ON (R.M_Plan_ID = M.M_Plan_ID)
    JOIN
        grants AS G ON (G.GID = R.GID)
    GROUP BY
        M.M_Plan_ID
)
SELECT
    M.M_Plan_ID
,   G.GID
FROM
    M_Plan_ID AS M
JOIN
    PlanRange ON (PlanRange.M_Plan_ID = M.M_Plan_ID)
CROSS JOIN
    grants AS G
LEFT OUTER JOIN
    rel AS R ON (
        R.M_Plan_ID = M.M_Plan_ID
    AND R.GID = G.GID
    )
WHERE
    G.active = 'Y'
AND G.start_date BETWEEN PlanRange.start_date AND PlanRange.end_date
AND R.M_Plan_ID IS NULL
ORDER BY
    M.M_Plan_ID, G.GID