如何写“连胜”查询?

问题描述 投票:0回答:2

我有一个名为 MyDates 的表,其中包含列 Id (INT) 和 MyDate (DATE)。 MyDates 可以包含具有相同日期的多行。

我想统计连续工作日的个数,从今天开始往回推,其中一个工作日定义为从周一到周五的任意一个工作日。

我尝试使用 cte(公用表表达式)编写查询,但我卡住了

假设今天是 2023 年 4 月 27 日 MyDates 表包含以下七行:

1, 2023-04-17
2, 2023-04-21
3, 2023-04-24
4, 2023-04-24
5, 2023-04-25
6, 2023-04-26
7, 2023-04-27

我希望查询返回 Streak = 5

那是因为连胜应该计算从 2023-14-27 星期四到 2023-04-24 星期一以及 2023-04-21 星期五这四个日期。

查询应该忽略星期六 2023-04-22 和星期日 2023-04-23,并且它应该忽略星期一 2023-04-24 的行

SELECT MIN(date) AS start_date, MAX(date) AS end_date, COUNT(*) AS streak_length
FROM (
  SELECT date, value, ROW_NUMBER() OVER (ORDER BY date) AS rn, 
         ROW_NUMBER() OVER (PARTITION BY value >= threshold, is_weekend ORDER BY date) AS grp
  FROM (
    SELECT date, value,
           CASE WHEN DAYOFWEEK(date) IN (1, 7) THEN 1 ELSE 0 END AS is_weekend
    FROM table_name
  ) sub
) sub
WHERE value >= threshold
GROUP BY value, grp - rn
ORDER BY start_date;
sql sql-server common-table-expression gaps-and-islands date-arithmetic
2个回答
1
投票

这是解决这个缺口和孤岛问题的分步方法。

我们将从删除日期开始,并将“上一个”日期与

lag()
相关联;为了以防万一,我们也可以排除周末的日期,如果有的话:

select date, lag(date) over(order by date) lag_date
from mytable
where datepart(weekday, date) not in (1, 7)
group by date

从那里开始,我们可以使用条件和来定义“相邻”记录组(又名islands);诀窍是在遇到周末时调整日期算法:

select t.*, 
    sum(
        case when date = dateadd(
            day, 
            case when datepart(weekday, lag_date) = 6 then 3 else 1 end,
            lag_date
        ) then 0 else 1 end) over(order by date desc) grp
from (
    select date, lag(date) over(order by date) lag_date
    from mytable
    group by date
) t
    

最后一步是只保留第一组(对应于最近的连胜):

select min(lag_date) streak_start, max(date) streak_end,
    count(*) + 1 streak_length
from (
    select t.*, 
        sum(
            case when date = dateadd(
                day, 
                case when datepart(weekday, lag_date) = 6 then 3 else 1 end,
                lag_date
            ) then 0 else 1 end) over(order by date desc) grp
    from (
        select date, lag(date) over(order by date) lag_date
        from mytable
        where datepart(weekday, date) not in (1, 7)
        group by date
    ) t
) t
where grp = 0
连胜开始 连胜结束 连胜长度
2023-04-21 2023-04-27 5

小提琴


重要说明:

dayofweek
,在您的原始查询中使用,在 SQL Server 中不是一个东西。我们改用
datepart(weekday, ...)
。但是请注意,此函数的结果取决于您如何设置
datefirst
参数。这里的答案假设
set datefirst 7
(这意味着日期周从星期日开始,正如您的原始代码所暗示的那样)。


0
投票

这是一个解决方案:

WITH cte AS
(
    SELECT  dt
    ,   (DATEPART(DW, dt) + @@DATEFIRST + 5) % 7 + 1 AS weekDay
    FROM    (
        VALUES  (N'2023-04-17')
        ,   (N'2023-04-21')
        ,   (N'2023-04-24')
        ,   (N'2023-04-24')
        ,   (N'2023-04-25')
        ,   (N'2023-04-26')
        ,   (N'2023-04-27')
    ) t (dt)
    )

SELECT  TOP 1 COUNT(DISTINCT dt), groupstreak
FROM    (
    SELECT  *
    ,   SUM(changeStreak) OVER(ORDER BY dt) AS groupStreak
    FROM    (
        SELECT  *
        ,   CASE WHEN datediff(day, LAG(dt) OVER(ORDER BY dt), dt) > CASE WHEN weekday = 1 THEN 3 ELSE 1 END THEN 1 ELSE 0 END AS changeStreak
        FROM    cte 
        WHERE   weekday BETWEEN 1 AND 5
        )x
    ) x
GROUP BY groupstreak
ORDER BY 1 DESC

我首先只计算工作日,然后检查与前一天的差异,如果它超过 1(或星期一超过 3),那么它被认为是连胜,最后我通过聚合连胜来分组,这给了我们独特的组。然后你只需要对组的最高计数

© www.soinside.com 2019 - 2024. All rights reserved.