【问题标题】:Return dates from last/this week depending on the current time and when "production" started上周/本周的返回日期取决于当前时间和“生产”开始的时间
【发布时间】:2023-03-28 13:15:01
【问题描述】:

对不起,如果标题令人困惑,但很难用一句话来解释我所追求的。

我目前正在根据本周的产量制作一份报告。我从以下内容开始我的 CTE 构建,以获得本周的周一至周五:

WITH
     cte_Date AS
     (
            SELECT
                   CAST(DateTime AS date) AS Date
            FROM
                   ( VALUES
                   (GETDATE()
                   )
                 , (DATEADD(day,-1,GETDATE()))
                 , (DATEADD(day,-2,GETDATE()))
                 , (DATEADD(day,-3,GETDATE()))
                 , (DATEADD(day,-4,GETDATE()))
                 , (DATEADD(day,-5,GETDATE()))
                 , (DATEADD(day,-6,GETDATE())) ) AS LastSevenDays(DateTime)
            WHERE
                   DATENAME(weekday, DateTime) = 'Monday'

            UNION ALL

            SELECT
                   DATEADD(day,1,Date)
            FROM
                   cte_Date
            WHERE
                   DATENAME(weekday,Date) <> 'Friday'
     )

这工作正常。我已将报告提供给用户,因此他们可以随时运行它,但有时周五没有人可以运行它。这意味着他们无法在周五看到完整的作品,然后下周 CTE 天数会发生变化。

我试图保持一键式事件,而不是引入我向用户建议我们调整查询的日期参数,以便如果他们在“星期一”中午之前运行报告,那么它将最后显示一周的数字,他们对此很满意(我和我的大嘴巴)。我将星期一放在引号中,因为我们真正的意思当然是一周的第一天生产

我的主数据表(我们将其称为 MyData)有一个名为 DateTime(真的!)的日期时间字段,我可以参考它来确定一周的第一天生产。

最后一个警告:由于报告的布局,用户坚持他们总是希望查看周一至周五的五天,即使在某一天没有生产。 (因此,我从 cte_Date 向所有其他所需的表执行 LEFT JOIN。)所以要清楚,现在我正在输入这个时间是当地时间周二上午 11:45,而昨天恰好是这里的公共假期,所以运行报告now 应该在上周的周一到周五返回,但在 20 分钟内运行它应该在本周的周一到周五返回。

请帮忙,我可怜的脑袋想弄明白。

【问题讨论】:

    标签: sql-server date common-table-expression


    【解决方案1】:

    有几种不同的方法可以解决这个问题,但它们都归结为同一件事:您需要一种方法来确定是在本周第一个工作日的中午 12 点之前还是之后,然后您需要获取当前“生产周”的星期一。

    我们只是说,为了简单起见,您有某种包含公共假期(或非生产日)的表。要确定是否是当前生产周的第一天,您基本上只需添加自该周开始以来的公共假期的连续天数。

    然后你需要弄清楚是在当天的中午 12 点之前还是之后。

    如果是在您想要上周的周一至周五之前。如果是之后,您想要本周的周一至周五。

    这是您可以这样做的一种方法:

    DECLARE @NonProductionDays TABLE (NPD DATE UNIQUE NOT NULL); -- Public holiday table. 
    INSERT @NonProductionDays (NPD) VALUES ('2017-09-25');
    
    
    DECLARE @i INT = -- You don't need a variable for this, but just to keep things simple...
    (
        SELECT COUNT(*) -- Extract number of public holidays in a row this week before current date. 
        FROM @NonProductionDays AS N
        WHERE DATEDIFF(WEEK, 0, N.NPD) = DATEDIFF(WEEK, 0, GETDATE())
        AND N.NPD <= GETDATE()
        AND (DATENAME(WEEKDAY, N.NPD) = 'Monday' OR EXISTS (SELECT 1 FROM @NonProductionDays AS N2 WHERE N2.NPD = DATEADD(DAY, -1, N.NPD)))
    );
    SELECT D = CAST(DATEADD(DAY, T.N, DATEADD(WEEK, DATEDIFF(HOUR, DATEADD(DAY, @i, '1900-01-01 12:00:00'), GETDATE()) / 24 / 7, '1900-01-01')) AS DATE)
    FROM (VALUES (0), (1), (2), (3), (4)) AS T(N);
    
    /*
        Breaking this down: 
            X = DATEADD(DAY, @i, '1900-01-01 12:00:00') 
                -- Adds the number of NPD days this week to '1900-01-01 12:00:00'
                -- So, for example, X would be '1900-01-02 12:00:00' this week
            Y = DATEDIFF(HOUR, X, GETDATE()) / 24 / 7 
                -- The number of weeks between X and now, by taking the number of hours and dividing by 24 then by 7 
                -- The division is necessary to compare the hour. 
                -- So, for example, as of 11am on the September 26 2017, you'd get 6142.
                -- As of 12pm on September 26 2017, you'd get 6143.
            Z = DATEADD(WEEK, Y, '1900-01-01')
                -- Just adds Y weeks to 1900-01-01, which was a Monday. This tells you the Monday of the current "production week".
                -- So, for example, as of 11am on September 26 2017, you'd get '2017-09-18 00:00:00.000'.
                -- As of 12pm on September 26 2017, you'd get '2017-09-25 00:00:00.000'.
    
            Then we cast this as a date and add 0/1/2/3/4 days to it to get Monday, Tuesday, Wednesday, Thursday and Friday of the current "production week".
    */
    

    【讨论】:

    • 感谢@ZLK 的详细回答。老实说,虽然这不是我所希望的。有时非生产日是零星的,因为它们发生的原因不仅仅是公共假期,例如严重的崩溃甚至糟糕的市场状况。也就是说,我完全同意你的第一段,也许我可以跳出那段来增强我原来的 CTE。
    • @WayneIvory 无论是否是公共假期,我认为拥有某种“非生产日”表或日期表是有帮助的,例如,每个工作日都有接下来的一百年左右,每当有“非生产日”发生时,你就会从中取出“非生产日”。无论如何,在零星的非生产日的情况下,有人需要手动更新某事,或者任何类型的报告怎么会知道不包括这一天?
    【解决方案2】:

    我不确定我是否想出了最有效的方法,但经过一个星期的折腾在我的脑海中,这就是我想出的。我从@ZLK 建议的相反方向解决了这个问题。

    我现有的逻辑已经给了我本周的星期一,所以在一个子查询中,我查找了星期一之后的第一条生产记录,用 DATEDIFF 去掉了时间,并用 DATEADD 使它成为了中午。然后,我可以将当​​前日期/时间与第一个生产日的中午进行比较,以确定是否将日期缩短一周。

    我替换了这个 SELECT 子句:

    SELECT
        CAST(DateTime AS date) AS Date
    

    用这个:

    SELECT -- Monday this week if it's after midday on the first production day otherwise Monday last week
            DATEADD(week,IIF(GETDATE()>=DATEADD(hour,12,(
                                                            SELECT DATEDIFF(day,0,MIN(DateTime)) 
                                                            FROM MyData 
                                                            WHERE CAST(MyData.DateTime AS date) >= CAST(LastSevenDays.DateTime AS date) 
                                                        )),0,-1),CAST(LastSevenDays.DateTime AS date)) AS Date
    

    为了满足新的一周已经开始但操作员在生产开始前运行报告的情况,我在 IIF 子句中仔细安排了布尔条件,以便子查询的空结果集意味着测试返回 FALSE 并且操作员仍会看到上周的数据。

    (@ZLK,感谢您的意见-您确实帮助了我的思考,但我认为您的答案不应该被标记为正确。我在这里提出的是我最初要求但没有t 需要使用静态表。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-19
      • 2022-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      • 1970-01-01
      相关资源
      最近更新 更多