【问题标题】:How to date add if end date is 1 day prior to the next rows start date如果结束日期比下一行开始日期早 1 天,如何添加日期
【发布时间】:2021-06-21 14:14:10
【问题描述】:

仅当第一行的结束日期是下一行开始日期的前一天时,我才尝试合计两行的天数。如果第一行的结束日期不是第二行开始日期的前一天,那么我想从查询中排除这两行。因此,对于下面的示例,我的总和应该是 365。我的活动表有数千行具有不同的名称和 orderid,我需要在执行此任务的同时保持每个人的 orderID 的完整性。

name orderID Start date end date
Joe Smith 1 2020-01-01 2020-09-30
Joe Smith 2 2020-10-01 2020-12-30

【问题讨论】:

  • 用您正在使用的数据库标记您的问题。 SSMS 建议使用 SQL Server。如果有多行符合您的条件怎么办?

标签: sql sql-server datediff dateadd


【解决方案1】:

基于@Gordon-Linoff 的回答和他关于间隙和孤岛的线索,但我在添加测试数据时遇到了错误,正如评论中提到的那样。我也用过这个帖子 https://bertwagner.com/posts/gaps-and-islands/

-- test data
DECLARE @t TABLE (name varchar(50), orderID int, StartDate DateTime, EndDate DateTime);

INSERT INTO @t
SELECT 'Joe Smith', 1, '2020-01-01', '2020-09-30' UNION
SELECT 'Joe Smith', 2, '2020-10-01', '2020-12-30' UNION
SELECT 'Joe Smith', 3, '2021-01-01', '2021-09-30' UNION
SELECT 'Joe Smith', 4, '2021-10-01', '2021-12-31' UNION
SELECT 'Joe Smith', 5, '2022-01-01', '2022-09-30' UNION
SELECT 'Jane Doe', 6, '2020-01-01', '2020-09-30' UNION
SELECT 'Jane Doe',  7, '2020-11-01', '2020-12-30';

-- caculate the difference add 1 because EndDate is inclusive (ends on the start of next day)
SELECT t.*, d.IslandStartDate, d.IslandEndDate, DATEDIFF(DAY, IslandStartDate, IslandEndDate) + 1 AS Days FROM (
-- return the minimum and maximum start and end dates 
SELECT
    name, 
    MIN(StartDate) AS IslandStartDate,
    MAX(EndDate) AS IslandEndDate
FROM (
    SELECT
        *,
        -- indicates when a new island begins by looking if the current row's StartDate occurs after the previous row's EndDate
        CASE WHEN Groups.PreviousEndDate >= DATEADD(DAY, -1, StartDate) THEN 0 ELSE 1 END AS IslandStartInd,
        -- indicates which island number the current row belongs to
        SUM(CASE WHEN Groups.PreviousEndDate >= DATEADD(DAY, -1, StartDate) THEN 0 ELSE 1 END) OVER (PARTITION BY name ORDER BY Groups.RN) AS IslandId
    FROM
    (
        -- create a row number column based on the sequence of start and end dates, as well as bring the previous row's EndDate to the current row
        SELECT
            name,
            orderID,
            ROW_NUMBER() OVER(PARTITION BY name ORDER BY StartDate,EndDate) AS RN,
            StartDate,
            EndDate,
            LAG(EndDate,1) OVER (PARTITION BY name ORDER BY StartDate, EndDate) AS PreviousEndDate
        FROM
            @t
    ) Groups
) Islands
GROUP BY
    name, IslandId
) d
-- join to get the orderID back
INNER JOIN @t t ON d.name = t.name AND t.StartDate >= d.IslandStartDate AND t.EndDate <= d.IslandEndDate
ORDER BY IslandStartDate, name

【讨论】:

    【解决方案2】:

    如果您想要满足您的条件的多行组,那么这是一种间隙和孤岛问题。

    您要做的是为行分配一个“孤岛”编号。您可以通过在上一行达到峰值来查看它是否符合您的条件。如果没有,则开始一个岛屿。岛屿起点的累积总和会为这些组分配一个岛屿编号。

    剩下的就是聚合

    select name, min(startdate), max(enddate),
           datediff(day, min(startdate), max(enddate)) as num_days
    from (select t.*,
                 sum(case when prev_end_date <> dateadd(day, -1, startdate) then 0 else 1 end) over 
                     (partition by name order by startdate) as island
          from (select t.*,
                       lag(end_date) over (partition by name order by start_date) as prev_end_date
                from t
               ) t
         ) t
    group by name, island
    having count(*) > 1;
    

    【讨论】:

    • 这是一个很好的起点,当我从我的服务器运行时效果很好。不幸的是,查询还在不应该的情况下将日期加在一起。例如,第 1 行的结束日期不是第 2 行开始日期的前一天,但是这两行是加在一起的。如果我遗漏了什么,我深表歉意。你的 SQL 技能比我强得多。
    • 第 1 行的结束日期是第 2 行开始日期的前一天。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    • 2018-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多