【问题标题】:How do I take a day of the year and 'bucket it' into weeks of the year in Microsoft SQL? Used in manufacturing scenarios for material requirements如何在 Microsoft SQL 中将一年中的一天“存储”到一年中的几周?用于材料要求的制造场景
【发布时间】:2010-09-29 23:33:25
【问题描述】:

我需要创建一个总需求报告,该报告从开始日期开始计算库存中物品的供应和需求量,并将其“存储”到一年中的不同周,以便物料计划人员知道他们什么时候需要一个项目,如果他们当时有足够的库存。

例如,今天的日期(报告日期)是 8/27/08。第一步是找到报告日期所在周的星期一的日期。在这种情况下,星期一将是 8/25/08。这成为第一个桶的第一天。在此之前的所有交易都分配到第 0 周,并将汇总为报表的期初余额。剩余的桶是从那个点开始计算的。对于第 8 个存储桶,没有结束日期,因此在第 8 个存储桶开始日期之后的任何交易都被视为第 8 周。

周# 开始日期 结束日期
0.......无........8/24/08
1.......8/25/08.......8/31/08
2.......9/1/08............9/7/08
3.......9/8/08.........9/14/08
4.......9/15/08.......9/21/08
5.......9/22/08.......9/28/08
6.......9/29/08.......10/5/08
7.......10/06/08.....10/12/08
8.......10/13/08......无

如何获取给定日期的周#、开始日期、结束日期?

【问题讨论】:

    标签: sql-server datediff dateadd


    【解决方案1】:

    --SQL 将一周的第一天设置为星期日,我们希望它是星期一。
    --这个命令就是这样做的。

    SET DATEFIRST 1
    
    DECLARE 
        @ReportDate DATETIME, 
    
        @Weekday INTEGER, 
        @NumDaysToMonday INTEGER, 
        @MondayStartPoint DATETIME,
        @MondayStartPointWeek INTEGER,
        @DateToProcess DATETIME,
        @DateToProcessWeek INTEGER,
        @Bucket VARCHAR(50),
        @DaysDifference INTEGER,
        @BucketNumber INTEGER,
        @NumDaysToMondayOfDateToProcess INTEGER,
        @WeekdayOfDateToProcess INTEGER,
        @MondayOfDateToProcess DATETIME,
        @SundayOfDateToProcess DATETIME
    
    SET @ReportDate = '2009-01-01'
    print @ReportDate
    
    SET @DateToProcess = '2009-01-26'
    --print @DateToProcess
    
    SET @Weekday = (select DATEPART ( dw , @ReportDate ))
    --print @Weekday
    
    --print DATENAME(dw, @ReportDate)
    
    SET @NumDaysToMonday = 
        (SELECT
          CASE 
             WHEN @Weekday =  1 THEN 0
             WHEN @Weekday =  2 THEN 1
             WHEN @Weekday =  3 THEN 2
             WHEN @Weekday =  4 THEN 3
             WHEN @Weekday =  5 THEN 4
             WHEN @Weekday =  6 THEN 5
             WHEN @Weekday =  7 THEN 6
          END)
    
    --print @NumDaysToMonday
    
    SET @MondayStartPoint =  (SELECT DATEADD (d , -1*@NumDaysToMonday, @ReportDate))
    --print @MondayStartPoint
    
    SET @DaysDifference = DATEDIFF ( dd , @MondayStartPoint , @DateToProcess )
    --PRINT @DaysDifference
    
    SET @BucketNumber = @DaysDifference/7
    --print @BucketNumber
    
    ----Calculate the start and end dates of this bucket------
    PRINT 'Start Of New Calc'
    
    print @DateToProcess
    
    SET @WeekdayOfDateToProcess = (select DATEPART ( dw , @DateToProcess ))
    print @WeekdayOfDateToProcess
    
    SET @NumDaysToMondayOfDateToProcess= 
        (SELECT
          CASE 
             WHEN @WeekdayOfDateToProcess =  1 THEN 0
             WHEN @WeekdayOfDateToProcess =  2 THEN 1
             WHEN @WeekdayOfDateToProcess =  3 THEN 2
             WHEN @WeekdayOfDateToProcess =  4 THEN 3
             WHEN @WeekdayOfDateToProcess =  5 THEN 4
             WHEN @WeekdayOfDateToProcess =  6 THEN 5
             WHEN @WeekdayOfDateToProcess =  7 THEN 6
          END)
    
    print @NumDaysToMondayOfDateToProcess
    SET @MondayOfDateToProcess =  (SELECT DATEADD (d , -1*@NumDaysToMondayOfDateToProcess, @DateToProcess))
    print @MondayOfDateToProcess   ---This is the start week
    
    SET @SundayOfDateToProcess = (SELECT DATEADD (d , 6, @MondayOfDateToProcess))
    PRINT @SundayOfDateToProcess
    

    【讨论】:

      【解决方案2】:

      我看到一次一个桶的方法的问题是它很难扩展,

      如果你加入一个用户定义的函数,你会得到更好的性能,你可以使用this a a starting point

      【讨论】:

      • 抱歉,我没有指定将它放在用户函数中。如果不是这样,它会使父程序非常混乱!我喜欢根据您帖子中的 cmets 建议将存储桶范围放入临时表中,然后从中查询,而不是一遍又一遍地调用该函数。
      • 确保你也为临时表添加了正确的索引,否则这可能会变成梨形。
      • 没有临时表 - 永久索引表。 UDF 和临时表都不好。 (过去,UDF 本质上是 SP,而您最终每行调用一个 SP - 虽然“编译”但很难看。希望他们已经减轻了一点。)
      • 我很想知道索引临时表和索引永久表之间的性能差异是什么......它应该只相当于它创建表、索引它并重新编译计划的时间.通常对于这类报告,您会查看几秒钟的执行时间
      • 主要好处是简化的执行单元。请参阅其他类似说明。
      【解决方案3】:

      我一直认为(对于 SQL Server)最简单和最有效的方法是在您的域范围内为未来每周构建一个包含一行的表;并加入其中(使用“WHERE GETDATE() >= MONDATE AND NOT EXISTS (SELECT 1 FROM table WHERE MONDATE

      你尝试用 UDF 做的任何事情都会效率低得多,而且我发现更难使用。

      【讨论】:

      • select * from dbo.fnGetDateRanges('d', '2006-01-01 11:10:00', '2006-01-05 08:00:00') 并不难使用 ... 一般来说,我发现 UDF 性能足够好,您也可以使用 UDF 填充水平表
      • 我一直发现 UDF 破坏了 SARG 和 TSQL 日期算术无论如何都需要掌握。但是,如果您对 TSQL 日期表达式不满意,那么对于构建表来说已经足够了。 YYMV。 :D
      • 我有足够的数据(大约 800,000 行),并且很容易从一开始就创建临时表或使用 UDF(已经构建)切换它,我可以测试两种方式来查看哪个更快。
      • 不要将其设为临时表 - 使其永久化,以便正确索引。让我们知道您的发现:)
      • 这里没有参数,UDF 效率较低,但根据问题,它们可以足够高效,并且代码有时可以更好地阅读,它们肯定比在一行中做的事情更有效率一次......
      【解决方案4】:

      您可以在一周中的任何给定日期获得星期一:

      DATEADD(d, 1 - DATEPART(dw, @date), @date)
      

      你可以用下面的body写一个存储过程

      -- find Monday at that week
      DECLARE @currentDate SMALLDATETIME
      SELECT @currentDate = DATEADD(d, 1 - DATEPART(dw, @date), @date)
      
      -- create a table and insert the first record
      DECLARE @weekTable TABLE (Id INT, StartDate SMALLDATETIME, EndDate SMALLDATETIME)
      INSERT INTO @weekTable VALUES (0, NULL, @currentDate)
      
      -- increment the date
      SELECT @currentDate = DATEADD(d, 1, @currentDate)
      
      -- iterate for 7 more weeks
      DECLARE @id INT
      SET @id = 1
      WHILE @id < 8
      BEGIN
          INSERT INTO @weekTable VALUES (@id, @currentDate, DATEADD(d, 6, @currentDate))
          SELECT @currentDate = DATEADD(ww, 1, @currentDate)
          SET @id = @id + 1
      END
      
      -- add the last record
      INSERT INTO @weekTable VALUES (8, @currentDate, NULL)
      
      -- select the values
      SELECT Id 'Week #', StartDate 'Start Date', EndDate 'End Date'
      FROM @weekTable
      

      当我通过时

      @date = '20080827'
      

      到这个过程,我得到以下

      Week #  Start Date     End Date
      0   NULL                2008-08-24 00:00:00
      1   2008-08-25 00:00:00 2008-08-31 00:00:00
      2   2008-09-01 00:00:00 2008-09-07 00:00:00
      3   2008-09-08 00:00:00 2008-09-14 00:00:00
      4   2008-09-15 00:00:00 2008-09-21 00:00:00
      5   2008-09-22 00:00:00 2008-09-28 00:00:00
      6   2008-09-29 00:00:00 2008-10-05 00:00:00
      7   2008-10-06 00:00:00 2008-10-12 00:00:00
      8   2008-10-13 00:00:00 NULL
      

      【讨论】:

        【解决方案5】:

        为什么不使用 DATEPART(year, date-column) 和 DATEPART(week, date-column) 的组合并按这些值分组。如果DATEPART 中的星期按照 ISO 8601 的要求在星期一对齐,则此方法有效。概述:

        SELECT DATEPART(year, date_column) AS yyyy,
               DATEPART(week, date_column) AS ww,
               ...other material as required...
            FROM SomeTableOrOther
            WHERE ...appropriate filters...
            GROUP BY yyyy, ww -- ...and other columns as necessary...
        

        【讨论】:

          猜你喜欢
          • 2020-02-02
          • 2011-09-06
          • 2019-11-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-06
          相关资源
          最近更新 更多