从多行日期中获取开始和结束日期,不包括周末
Get Start and End date from multiple rows of dates, excluding weekends
我正在尝试根据下面的数据 return 开始日期和结束日期 table:
Name
Date From
Date To
A
2022-01-03
2022-01-03
A
2021-12-29
2021-12-31
A
2021-12-28
2021-12-28
A
2021-12-27
2021-12-27
A
2021-12-23
2021-12-24
A
2021-11-08
2021-11-09
我想要的结果是这样的:
Name
Date From
Date To
A
2021-12-23
2022-01-03
A
2021-11-08
2021-11-09
第一个 table 中的日期有时会与周末一起使用日期从和日期到,但如果该行在星期五结束而下一行在下周一开始,则需要对其进行分类作为相同的“块”,如第二个 table 中所示。我希望根据 How do I exclude Weekend days in a SQL Server query? 使用 DATEFIRST
设置来满足周末的需求,以避免使用日历 table,但如果日历 table 最终成为最简单的出路我很高兴考虑创建一个。
在上面的示例中,我只有 1 个名称,但是 table 将有多个名称,需要按名称分组。
我看到的唯一例子是只使用 1 个日期列作为记录,我努力改变他们的代码来满足我的例子。我找到的最接近的示例对我不起作用,因为它基于日期时间字段和时差 - find start and stop date for contiguous dates in multiple rows
这是一个 Gaps & Island 问题,需要考虑周末的连续性。
你可以这样做:
select max(name) as name, min(date_from) as date_from, max(date_to) as date_to
from (
select *, sum(inc) over(order by date_to) as grp
from (
select *,
case when lag(ext_to) over(order by date_to) = date_from
then 0 else 1 end as inc
from (
select *,
case when (datepart(weekday, date_to) = 6)
then dateadd(day, 3, date_to)
else dateadd(day, 1, date_to) end as ext_to
from t
) x
) y
) z
group by grp
结果:
name date_from date_to
---- ---------- ----------
A 2021-11-08 2021-11-09
A 2021-12-23 2022-01-03
请参阅 db<>fiddle #1 中的 运行 示例。
注意:你的问题没有提到,但你可能想按人细分。我没有做。
编辑:按名称添加分区
按名称分区实际上很容易。以下查询执行此操作:
select name, min(date_from) as date_from, max(date_to) as date_to
from (
select *, sum(inc) over(partition by name order by date_to) as grp
from (
select *,
case when lag(ext_to) over(partition by name order by date_to) = date_from
then 0 else 1 end as inc
from (
select *,
case when (datepart(weekday, date_to) = 6)
then dateadd(day, 3, date_to)
else dateadd(day, 1, date_to) end as ext_to
from t
) x
) y
) z
group by name, grp
order by name, grp
请参阅 db<>fiddle #2 处的 运行 查询。
with extended as (
select name,
date_from,
case when datepart(weekday, date_to) = 6
then dateadd(day, 2, date_to) else date_to end as date_to
from t
), adjacent as (
select *,
case when dateadd(day, 1,
lag(date_to) over (partition by name order by date_from)) = date_from
then 0 else 1 end as brk
from extended
), blocked as (
select *, sum(brk) over (partition by name order by date_from) as grp
from adjacent
)
select name, min(date_from), max(date_to) from blocked
group by name, grp;
我假设范围没有重叠并且所有输入日期都在工作日。在我的手机上敲定这个时,我最初犯了两个错误。出于某种原因,我在脑海中颠倒了往返日期,然后我认为星期五是 5 日(与 @@datefirst
一样)而不是 6 日。(当然,无论如何这可能会因区域设置而异。)一个使用 table 表达式的优点是模块化并将某些细节隐藏在较低级别的逻辑中。在这种情况下,如果其中一些假设被证明是错误的,调整日期将非常容易。
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=42e0c452d57d474232bcf991d6d3c43c
我正在尝试根据下面的数据 return 开始日期和结束日期 table:
Name | Date From | Date To |
---|---|---|
A | 2022-01-03 | 2022-01-03 |
A | 2021-12-29 | 2021-12-31 |
A | 2021-12-28 | 2021-12-28 |
A | 2021-12-27 | 2021-12-27 |
A | 2021-12-23 | 2021-12-24 |
A | 2021-11-08 | 2021-11-09 |
我想要的结果是这样的:
Name | Date From | Date To |
---|---|---|
A | 2021-12-23 | 2022-01-03 |
A | 2021-11-08 | 2021-11-09 |
第一个 table 中的日期有时会与周末一起使用日期从和日期到,但如果该行在星期五结束而下一行在下周一开始,则需要对其进行分类作为相同的“块”,如第二个 table 中所示。我希望根据 How do I exclude Weekend days in a SQL Server query? 使用 DATEFIRST
设置来满足周末的需求,以避免使用日历 table,但如果日历 table 最终成为最简单的出路我很高兴考虑创建一个。
在上面的示例中,我只有 1 个名称,但是 table 将有多个名称,需要按名称分组。
我看到的唯一例子是只使用 1 个日期列作为记录,我努力改变他们的代码来满足我的例子。我找到的最接近的示例对我不起作用,因为它基于日期时间字段和时差 - find start and stop date for contiguous dates in multiple rows
这是一个 Gaps & Island 问题,需要考虑周末的连续性。
你可以这样做:
select max(name) as name, min(date_from) as date_from, max(date_to) as date_to
from (
select *, sum(inc) over(order by date_to) as grp
from (
select *,
case when lag(ext_to) over(order by date_to) = date_from
then 0 else 1 end as inc
from (
select *,
case when (datepart(weekday, date_to) = 6)
then dateadd(day, 3, date_to)
else dateadd(day, 1, date_to) end as ext_to
from t
) x
) y
) z
group by grp
结果:
name date_from date_to
---- ---------- ----------
A 2021-11-08 2021-11-09
A 2021-12-23 2022-01-03
请参阅 db<>fiddle #1 中的 运行 示例。
注意:你的问题没有提到,但你可能想按人细分。我没有做。
编辑:按名称添加分区
按名称分区实际上很容易。以下查询执行此操作:
select name, min(date_from) as date_from, max(date_to) as date_to
from (
select *, sum(inc) over(partition by name order by date_to) as grp
from (
select *,
case when lag(ext_to) over(partition by name order by date_to) = date_from
then 0 else 1 end as inc
from (
select *,
case when (datepart(weekday, date_to) = 6)
then dateadd(day, 3, date_to)
else dateadd(day, 1, date_to) end as ext_to
from t
) x
) y
) z
group by name, grp
order by name, grp
请参阅 db<>fiddle #2 处的 运行 查询。
with extended as (
select name,
date_from,
case when datepart(weekday, date_to) = 6
then dateadd(day, 2, date_to) else date_to end as date_to
from t
), adjacent as (
select *,
case when dateadd(day, 1,
lag(date_to) over (partition by name order by date_from)) = date_from
then 0 else 1 end as brk
from extended
), blocked as (
select *, sum(brk) over (partition by name order by date_from) as grp
from adjacent
)
select name, min(date_from), max(date_to) from blocked
group by name, grp;
我假设范围没有重叠并且所有输入日期都在工作日。在我的手机上敲定这个时,我最初犯了两个错误。出于某种原因,我在脑海中颠倒了往返日期,然后我认为星期五是 5 日(与 @@datefirst
一样)而不是 6 日。(当然,无论如何这可能会因区域设置而异。)一个使用 table 表达式的优点是模块化并将某些细节隐藏在较低级别的逻辑中。在这种情况下,如果其中一些假设被证明是错误的,调整日期将非常容易。
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=42e0c452d57d474232bcf991d6d3c43c