我有一个协议表和一个计划表。计划是协议的子女,并有FK参考。
同一协议的所有计划都应该填补协议的整个范围。但是,协议中有一些缺失的计划。
在SQL或SSIS中是否还有一个"缺少"的列表?计划?
协议表
+--------------+----------------------+--------------------+ | Agreement Id | Agreement Start Date | Agreement End Date | +--------------+----------------------+--------------------+ | 1 | 1/1/2010 | 12/31/2016 | +--------------+----------------------+--------------------+
计划表
+--------------+---------+-----------------+---------------+ | Agreement Id | Plan Id | Plan Start Date | Plan End Date | +--------------+---------+-----------------+---------------+ | 1 | 1 | 1/1/2010 | 12/31/2010 | | 1 | 2 | 1/1/2012 | 12/31/2012 | | 1 | 3 | 1/1/2014 | 12/31/2016 | +--------------+---------+-----------------+---------------+
所需的计划表
+--------------+---------+-----------------+---------------+ | Agreement Id | Plan Id | Plan Start Date | Plan End Date | +--------------+---------+-----------------+---------------+ | 1 | 1 | 1/1/2010 | 12/31/2010 | | 1 | 4 | 1/1/2011 | 12/31/2011 | | 1 | 2 | 1/1/2012 | 12/31/2012 | | 1 | 5 | 1/1/2013 | 12/31/2013 | | 1 | 3 | 1/1/2014 | 12/31/2016 | +--------------+---------+-----------------+---------------+
基本上,我想得到协议1缺失的计划,即这些行:
+--------------+---------+-----------------+---------------+ | Agreement Id | Plan Id | Plan Start Date | Plan End Date | +--------------+---------+-----------------+---------------+ | 1 | 4 | 1/1/2011 | 12/31/2011 | | 1 | 5 | 1/1/2013 | 12/31/2013 | +--------------+---------+-----------------+---------------+
答案 0 :(得分:1)
这是为MS SQL Server编写的,因此如果您正在为MySQL编码,则可能需要调整日期函数,但我相信这应该可行。我不认为我在协议开始时已经涵盖了缺失计划的情况,因此我会尽快给出一些想法并添加代码:
SELECT
DATEADD(DAY, 1, P.end_date) AS start_date,
COALESCE(DATEADD(DAY, -1, P3.start_date), A.end_date) AS end_date
FROM
dbo.Agreements A
INNER JOIN dbo.Plans P ON
P.agreement_id = A.agreement_id
LEFT OUTER JOIN dbo.Plans P2 ON P2.agreement_id = A.agreement_id AND P2.start_date = DATEADD(DAY, 1, P.end_date)
LEFT OUTER JOIN dbo.Plans P3 ON P3.agreement_id = A.agreement_id AND P3.start_date > P.end_date
LEFT OUTER JOIN dbo.Plans P4 ON P4.agreement_id = A.agreement_id AND P4.start_date BETWEEN P.end_date AND P3.start_date AND P4.plan_id <> P3.plan_id
WHERE
P.end_date <> A.end_date AND
P2.agreement_id IS NULL AND
P4.agreement_id IS NULL
此方法还应捕获缺少的开始和结束计划,但使用窗口函数ROW_NUMBER来排序。你可以在没有ROW_NUMBER的情况下做到这一点,但它要复杂得多。我也不确定在SQL中没有更简单的方法,但这是我开始输入时首先想到的:
;WITH CTE_MissingEndDates AS
(
SELECT agreement_id, missing_end_date, ROW_NUMBER() OVER (PARTITION BY agreement_id ORDER BY missing_end_date) AS row_num
FROM
(
SELECT
A.agreement_id,
DATEADD(DAY, -1, P1.start_date) AS missing_end_date
FROM
dbo.Agreements A
INNER JOIN dbo.Plans P1 ON P1.agreement_id = A.agreement_id
LEFT OUTER JOIN dbo.Plans P2 ON P2.agreement_id = A.agreement_id AND P2.end_date = DATEADD(DAY, -1, P1.start_date)
WHERE
P1.start_date > A.start_date
UNION
SELECT A2.agreement_id, A2.end_date FROM dbo.Agreements A2 WHERE NOT EXISTS (SELECT * FROM dbo.Plans WHERE agreement_id = A2.agreement_id AND end_date = A2.end_date)
) SQ
),
CTE_MissingStartDates AS
(
SELECT agreement_id, missing_start_date, ROW_NUMBER() OVER (PARTITION BY agreement_id ORDER BY missing_start_date) AS row_num
FROM
(
SELECT
A.agreement_id,
DATEADD(DAY, 1, P1.end_date) AS missing_start_date
FROM
dbo.Agreements A
INNER JOIN dbo.Plans P1 ON P1.agreement_id = A.agreement_id
LEFT OUTER JOIN dbo.Plans P2 ON P2.agreement_id = A.agreement_id AND P2.start_date = DATEADD(DAY, 1, P1.end_date)
WHERE
P1.end_date < A.end_date
UNION
SELECT A2.agreement_id, A2.start_date FROM dbo.Agreements A2 WHERE NOT EXISTS (SELECT * FROM dbo.Plans WHERE agreement_id = A2.agreement_id AND start_date = A2.start_date)
) SQ
)
SELECT
MSD.missing_start_date,
MED.missing_end_date
FROM
CTE_MissingStartDates MSD
INNER JOIN CTE_MissingEndDates MED ON
MED.agreement_id = MSD.agreement_id AND
MED.row_num = MSD.row_num