【问题标题】:SQL code to add missing StartDate between 2 dates in SQL Server在 SQL Server 中的 2 个日期之间添加缺少的开始日期的 SQL 代码
【发布时间】:2022-12-25 00:43:05
【问题描述】:

我有按 clientID、contractID 和 effectiveDate 排序的下表。一个客户有多个 contractID 和它各自的 effectiveDate。

所需的输出如下,其中新的 FYStartDate 列应在 clientID 的后续 contractID 的 2 个日期之间添加缺失的 FYStartDate(在这种情况下,财政年度从每年的 01June 开始)

如果您能分享所需的 SQL 代码,我将不胜感激。

我附上了生成第一个表的 SQL 代码

CREATE TABLE [client] (
[clientid] [int] NULL,
[contractid] [int] NULL,
[effectivedate] [date] NULL
) ON [PRIMARY]
GO 

insert into [client] values
('228','2','6/1/2003'),('228','136','6/1/2004'),('228','242','6/1/2008'), 
('228','337','12/1/2012'),('228','584','6/1/2017'),('14216','319','5/1/2013'), 
('14216','355','6/1/2013'),('14216','739','6/1/2020'),('14216','10','3/1/2021'), 
('14216','1009','6/1/2021')

【问题讨论】:

  • 您有存储财政年度的日期表吗?(你应该这样做。只需加入那个“日历”表,你就可以解决一半的问题。)
  • 如果合同在年中被新合同取代,为什么两个合同都没有当年的条目? (财政年度第一部分的第一份合同,同一财政年度第二部分的第二份合同?)
  • 有一个日期表,但没有与 FY 相关的列。我已经创建了所需的 FY 相关列,并且正在使用指向包含所需日期表的 Excel 文件的链接来更新问题。如果我得到了想要的输出,我会建议客户将 FY 列添加到 Date 表中。
  • 请不要使用指向外部文档、图像等的链接。将相关信息作为格式化文本直接复制到您的问题中。 Please do not upload images of code/data/errors.
  • 我不会点击链接的 Excel 文档。

标签: sql sql-server


【解决方案1】:

没有@MatBailie 建议的关于拥有更多结构化数据的建议,这有点令人费解。为完成您的要求,每条记录都需要知道它之前和之后的合同何时生效。我认为您需要尝试排序,因为我不太了解如何对结果进行排序……按 clientid、contractid、日期等?

更新:见cmets。通过 clientid 更改了一些 CTE、JOINS 和 ORDER BY 以更好地进行分区。

CREATE TABLE [client] (
[clientid] [int] NULL,
[contractid] [int] NULL,
[effectivedate] [date] NULL
) ON [PRIMARY]
;

insert into [client] values
('228','2','6/1/2003'),('228','136','6/1/2004'),('228','242','6/1/2008'), 
('228','337','12/1/2012'),('228','584','6/1/2017'),('14216','319','5/1/2013'), 
('14216','355','6/1/2013'),('14216','739','6/1/2020'),('14216','10','3/1/2021'), 
('14216','1009','6/1/2021')
;

--Need a sequence of numbers to create a sequence of fiscal years.
WITH x AS (
  SELECT * FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) as x(a)
), y as (
  SELECT ROW_NUMBER() OVER(ORDER BY tens.a, ones.a) as row_num
  FROM x as ones, x as tens
), fiscYears as (
  SELECT
    fyStart = DATEFROMPARTS(2000 + y.row_num -1, 6, 1)
    , fyEnd = DATEFROMPARTS(2000 + y.row_num, 5, 31)
  FROM y

--Need to order the client records by effective date.
--From updated question... looks like we are reporting by clientid.
), clientOrd as (
  SELECT c2.*, ROW_NUMBER() OVER(PARTITION BY c2.clientid ORDER BY c2.effectivedate) as row_num
      FROM client c2

--For each contract, get the previous and next contracts by effective date.
), clientWNext as (
  SELECT c.*
    , cNext.effectivedate as nextEffectiveDate
    , cPrev.effectivedate as prevEffectiveDate
  FROM clientOrd as c
    LEFT JOIN clientOrd as cNext
      ON cNext.clientid = c.clientid
      AND cNext.row_num = c.row_num + 1
    LEFT JOIN clientOrd as cPrev
      ON cPrev.clientid = c.clientid
      AND cPrev.row_num = c.row_num - 1
)
SELECT
  c.clientid
  , cwn.contractid
  , CASE WHEN cwn.effectiveDate >= fy.fyStart AND cwn.effectiveDate <= fy.fyEnd 
      THEN  cwn.effectivedate
      ELSE null
    END as effectivedate
  , fy.fyStart
FROM fiscYears as fy
  --To get a full FY range for each client, we join to a distinct list of clients.
  JOIN (
    SELECT DISTINCT clientid FROM client
  ) as c
    ON 1=1

  --Need to join the list of contracts.
  INNER JOIN clientWNext as cwn
    ON cwn.clientid = c.clientid
  
    --This is the main join criteria where the effective date is within the fy year start/end.
    AND ((
      cwn.effectivedate >= fy.fyStart
      AND cwn.effectivedate <= fy.fyEnd
    ) 

    --This is the "alternate" join criteria where the previous contrat is still in effect
    --but there is no new contract to supercede the previous.
    OR (
      cwn.prevEffectiveDate < fy.fyStart
      AND cwn.effectiveDate < fy.fyStart
      AND (cwn.nextEffectiveDate > fy.fyEnd OR cwn.nextEffectiveDate IS NULL)
    ))

--Limiting fiscal year date range.
WHERE fy.fyStart >= '1/1/2003'
  AND fy.fyStart < '1/1/2024'
ORDER BY c.clientid, fy.fyStart, cwn.effectivedate


clientid contractid effectivedate fyStart
228 2 2003-06-01 2003-06-01
228 136 2004-06-01 2004-06-01
228 136 null 2005-06-01
228 136 null 2006-06-01
228 136 null 2007-06-01
228 242 2008-06-01 2008-06-01
228 242 null 2009-06-01
228 242 null 2010-06-01
228 242 null 2011-06-01
228 337 2012-12-01 2012-06-01
228 337 null 2013-06-01
228 337 null 2014-06-01
228 337 null 2015-06-01
228 337 null 2016-06-01
228 584 2017-06-01 2017-06-01
228 584 null 2018-06-01
228 584 null 2019-06-01
228 584 null 2020-06-01
228 584 null 2021-06-01
228 584 null 2022-06-01
228 584 null 2023-06-01
14216 319 2013-05-01 2012-06-01
14216 355 2013-06-01 2013-06-01
14216 355 null 2014-06-01
14216 355 null 2015-06-01
14216 355 null 2016-06-01
14216 355 null 2017-06-01
14216 355 null 2018-06-01
14216 355 null 2019-06-01
14216 739 2020-06-01 2020-06-01
14216 10 2021-03-01 2020-06-01
14216 1009 2021-06-01 2021-06-01
14216 1009 null 2022-06-01
14216 1009 null 2023-06-01

fiddle

【讨论】:

  • 非常感谢您的解决方案。输出几乎符合要求。 ORDER BY 序列是 c.clientid,c.effectivedate。唯一缺少的部分是,在 ClientID:14216、ContractID:355 和 ClientID:14216、ContractID:739 之间缺少 4 个财年开始日期 (17、18、19)
  • 同时,我用一个指向 Excel 文件的链接更新了我的问题,该文件包含带有所需 FY 列的 DimDate。你能推荐一个使用这个 DimDate 的解决方案吗?
  • @Vickar 使用您更新的排序标准,我更新了答案。由于 FY 范围似乎是 clientid 的独立报告,我更新了 clientOrdclientNext CTE 以包括 clientid 的分区。还更新了主 SELECT ... JOINs 以包含 clientid。为了在建立任何合同之前过滤掉 FY,我将 LEFT OUTER JOIN clientWNext 更改为 INNER JOIN。最后,我根据您上面的评论更新了ORDER BY
猜你喜欢
  • 1970-01-01
  • 2018-01-09
  • 1970-01-01
  • 2019-05-01
  • 2019-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多