You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确设置Round列?基于工作日规则的SQL查询修正需求

修正工作日间隙计算的SQL查询

数据集

CC ReceiverDate
DIAB21C0542023-01-03
DIAB21C0542023-01-04
DIAB21C0542023-01-05
DIAB21C0542023-01-11
DIAB21C0542023-01-12
DIAB21C0542023-01-18
DIAB21C0542023-01-19
DIAB21C0542023-01-20
DIAB21C0542023-01-26
DIAB21C0542023-01-27

Round列核心规则

  • 工作日为周一至周六
  • 周日为非工作日
  • 节假日同样为非工作日
  • 仅当连续两个日期之间的工作日间隙(忽略周日和节假日)超过1天时,Round值才变更

节假日表

HolidayDate
2023-01-01
2023-01-22
2023-01-23
2023-02-18
2023-03-22
2023-04-07
2023-04-21
2023-04-22
2023-04-23
2023-04-25
2023-04-26
2023-05-01
2023-05-18
2023-06-01
2023-06-02
2023-06-04
2023-06-29
2023-07-07
2023-07-19
2023-08-17
2023-09-28
2023-12-25
2023-12-26
2024-01-01
2024-02-08
2024-02-09
2024-02-10
2024-02-14
2024-03-08
2024-03-09
2024-03-11
2024-03-12
2024-03-15
2024-03-29
2024-03-31
2024-04-10
2024-04-11
2024-05-01
2024-05-09
2024-05-10
2024-05-23
2024-05-24
2024-06-01
2024-06-17
2024-06-18
2024-07-07
2024-08-17
2024-09-16
2024-12-25
2024-12-26

原SQL查询

WITH UniqueEntries AS (
    SELECT DISTINCT
        [CC Receiver],
        [Date]
    FROM
        combined_output_zpay_view_harvesting
),
RankedEntries AS (
    SELECT 
        [CC Receiver],
        [Date],
        ROW_NUMBER() OVER (PARTITION BY [CC Receiver] ORDER BY [Date]) AS RowNum
    FROM 
        UniqueEntries
),
-- Step 1: Calculate the number of working days between consecutive dates
WorkingDayDifference AS (
    SELECT
        r1.[CC Receiver],
        r1.[Date],
        r2.[Date] AS NextDate,
        DATEDIFF(DAY, r1.[Date], r2.[Date]) - 
        (
            SELECT COUNT(*) 
            FROM master.dbo.spt_values v
            WHERE v.type = 'P' 
            AND DATEADD(DAY, v.number, r1.[Date]) < r2.[Date]
            AND DATENAME(WEEKDAY, DATEADD(DAY, v.number, r1.[Date])) NOT IN ('Sunday') 
            AND NOT EXISTS (SELECT 1 FROM holidays h WHERE h.HolidayDate = DATEADD(DAY, v.number, r1.[Date]))
        ) AS WorkingDaysDiff
    FROM 
        RankedEntries r1
    LEFT JOIN RankedEntries r2
        ON r1.[CC Receiver] = r2.[CC Receiver] 
        AND r1.RowNum + 1 = r2.RowNum
),
-- Step 2: Identify when a new round starts
Rounds AS (
    SELECT
        wd.[CC Receiver],
        wd.[Date],
        CASE
            WHEN wd.WorkingDaysDiff > 1 THEN 1
            ELSE 0
        END AS IsNewRound
    FROM 
        WorkingDayDifference wd
    WHERE wd.NextDate IS NOT NULL
),
-- Step 3: Accumulate the round number
FinalRounds AS (
    SELECT
        [CC Receiver],
        [Date],
        SUM(IsNewRound) OVER (PARTITION BY [CC Receiver] ORDER BY [Date]) + 1 AS Round
    FROM 
        Rounds
)
SELECT
    [CC Receiver],
    [Date],
    Round
FROM
    FinalRounds
WHERE [CC Receiver] = 'DIAB21C054'
ORDER BY
    [CC Receiver], [Date];

错误结果

CC ReceiverDateRound
DIAB21C0542023-01-031
DIAB21C0542023-01-041
DIAB21C0542023-01-051
DIAB21C0542023-01-111
DIAB21C0542023-01-121
DIAB21C0542023-01-181
DIAB21C0542023-01-191
DIAB21C0542023-01-202
DIAB21C0542023-01-262
DIAB21C0542023-01-272

正确预期结果

CC ReceiverDateRound
DIAB21C0542023-01-031
DIAB21C0542023-01-041
DIAB21C0542023-01-051
DIAB21C0542023-01-112
DIAB21C0542023-01-122
DIAB21C0542023-01-183
DIAB21C0542023-01-193
DIAB21C0542023-01-203
DIAB21C0542023-01-264
DIAB21C0542023-01-274

另一错误案例

CC ReceiverDateRound
DIAB21C0542024-02-1014
DIAB21C0542024-02-1215

错误原因:2月10日为周六,2月12日为周一,周日为非工作日,两者的工作日间隙仅为1天,应属于同一Round。


修正方案

错误根源

原SQL的WorkingDaysDiff计算逻辑完全颠倒:它用总天数减去工作日数量,得到的是非工作日数量,而非我们需要的两个日期之间的工作日间隙。正确逻辑是计算连续记录日期之间的工作日天数(忽略周日和节假日),若该天数超过1则开启新Round。

修正后的SQL

WITH UniqueEntries AS (
    SELECT DISTINCT
        [CC Receiver],
        [Date]
    FROM
        combined_output_zpay_view_harvesting
),
RankedEntries AS (
    SELECT 
        [CC Receiver],
        [Date],
        ROW_NUMBER() OVER (PARTITION BY [CC Receiver] ORDER BY [Date]) AS RowNum
    FROM 
        UniqueEntries
),
-- 计算连续日期之间的工作日间隙:从当前日期次日到下一个日期的工作日数量
WorkingDayGap AS (
    SELECT
        r1.[CC Receiver],
        r1.[Date],
        r2.[Date] AS NextDate,
        (SELECT COUNT(*) 
         FROM master.dbo.spt_values v
         WHERE v.type = 'P' 
         AND DATEADD(DAY, v.number, DATEADD(DAY, 1, r1.[Date])) <= r2.[Date]
         AND DATENAME(WEEKDAY, DATEADD(DAY, v.number, DATEADD(DAY, 1, r1.[Date]))) != 'Sunday'
         AND NOT EXISTS (SELECT 1 FROM holidays h WHERE h.HolidayDate = DATEADD(DAY, v.number, DATEADD(DAY, 1, r1.[Date])))
        ) AS WorkingDaysGap
    FROM RankedEntries r1
    LEFT JOIN RankedEntries r2
        ON r1.[CC Receiver] = r2.[CC Receiver] 
        AND r1.RowNum + 1 = r2.RowNum
),
-- 判断是否开启新Round
RoundFlags AS (
    SELECT
        [CC Receiver],
        [Date],
        CASE
            WHEN WorkingDaysGap > 1 THEN 1
            ELSE 0
        END AS IsNewRound
    FROM WorkingDayGap
),
-- 累计Round编号
FinalRounds AS (
    SELECT
        [CC Receiver],
        [Date],
        SUM(IsNewRound) OVER (PARTITION BY [CC Receiver] ORDER BY [Date]) + 1 AS Round
    FROM RoundFlags
)
SELECT
    [CC Receiver],
    [Date],
    Round
FROM FinalRounds
WHERE [CC Receiver] = 'DIAB21C054'
ORDER BY [CC Receiver], [Date];

高效写法(SQL Server 2022+)

若使用SQL Server 2022及以上版本,可替换master.dbo.spt_values为更清晰的GENERATE_SERIES:

-- 替换WorkingDayGap中的子查询
(SELECT COUNT(*) 
 FROM GENERATE_SERIES(1, DATEDIFF(DAY, r1.[Date], r2.[Date])) AS s(n)
 CROSS APPLY (SELECT DATEADD(DAY, s.n, r1.[Date]) AS GapDate) AS gd
 WHERE DATENAME(WEEKDAY, gd.GapDate) != 'Sunday'
 AND NOT EXISTS (SELECT 1 FROM holidays h WHERE h.HolidayDate = gd.GapDate)
) AS WorkingDaysGap

内容的提问来源于stack exchange,提问作者Fachry Dzaky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 06:07:03