【问题标题】:First Monday of the Year in SQL ServerSQL Server 一年中的第一个星期一
【发布时间】:2012-11-23 08:58:07
【问题描述】:

如何使用 T-SQL 找到“一年中的第一个星期一”?

【问题讨论】:

  • 当前年份,还是要指定年份?
  • 其实没关系,我正在努力寻找逻辑,假设是带参数的。

标签: sql sql-server tsql


【解决方案1】:

这是 ngruson 发布的链接中的示例 (http://sqlbump.blogspot.nl/2010/01/first-monday-of-year.html):

DECLARE @Date datetime
DECLARE @Year int = 2012

SET @Date = DATEADD(YEAR, @Year - 1900, 0)

SELECT DATEADD(DAY, (@@DATEFIRST - DATEPART(WEEKDAY, @Date) + 
    (8 - @@DATEFIRST) * 2) % 7, @Date)

以上返回:

2012-01-02 00:00:00.000

【讨论】:

    【解决方案2】:
        Here, 
        1) DATEPART function for finding day of week. 
    Here 1=sunday,2=monday,..,7=saturday.
        2) first find out is 2(monday) in case when
     condition then get same date,
        3) if is sunday(1) then add 1 day and get next
     day date is for monday,
        4) if is not 2(monday) and greater then 2 then find 
    difference between lastday(7) and add 2 days ,
     so we reach to monday date.
    
        Here are you change year in @year variable. Ex:
    
            DECLARE @Date datetime
            DECLARE @Year int = 2012
    
            set @Date= convert(varchar(4),@Year) + '-01-01'
            select @Date,(case when DATEPART(DW,@Date)=2 then @Date
                         when DATEPART(DW,@Date)=1 then DATEADD(day,1,@Date)
                         else DATEADD(day,7-DATEPART(DW,@Date)+2,@Date)
                          end) as MondayDateis
    

    【讨论】:

      【解决方案3】:

      优雅不是;)

          DECLARE @year DATETIME = '01 jan 2012'
      
          SELECT [Day] 
          FROM
              (
                  SELECT @year [Day] UNION ALL
                  SELECT DATEADD(DAY, 1, @year) UNION ALL
                  SELECT DATEADD(DAY, 2, @year) UNION ALL
                  SELECT DATEADD(DAY, 3, @year) UNION ALL
                  SELECT DATEADD(DAY, 4, @year) UNION ALL
                  SELECT DATEADD(DAY, 5, @year) UNION ALL
                  SELECT DATEADD(DAY, 6, @year)
              ) x
          WHERE
              DATENAME(DW, [Day]) = 'Monday'
      

      【讨论】:

      • ...如果语言不是英语?
      • 那么一年中的第一个星期一显然
      【解决方案4】:

      这里有一个例子来实现这一点:

      http://sqlbump.blogspot.nl/2010/01/first-monday-of-year.html

      【讨论】:

        【解决方案5】:

        试试这个:

        declare @yr int=2020
        select case when datepart(weekday,dateadd(year,@yr-1900,0))=1 then dateadd(year,@yr-1900,1)
               else dateadd(dd,8-(datepart(weekday,dateadd(year,@yr-1900,0))),dateadd(year,@yr-1900,1))
        end
        

        SQL Fiddle demo

        【讨论】:

        • 你好。如果您运行 2013 年的代码,它会说第一个星期一是第 8 个(星期二)。这是一个小点,我知道...
        • 嗨。我会尝试更具体。您建议的代码不适用于 @@datefirst 的所有值(例如,请参阅接受的答案)。如果您的 SQL Server 环境将一周的第一天设置为星期日(美国默认),您将得到答案。但是,如果您将一周的第一天更改为其他日期,例如星期一,您会得到不同的答案。
        【解决方案6】:

        这种方法不使用像 1900 年这样的魔法年份或魔法日期。

        DECLARE @first_year_day     DATE,
                @first_year_monday  DATE,
                @year               int = 2017
        
        SET @first_year_day    = CAST('1/1/' + str(@year, 4) AS DATE)
        SET @first_year_monday    = DATEADD(day, (9 - DATEPART(dw, @first_year_day)) % 7, @first_year_day)
        SELECT @first_year_monday
        

        仅当在 SQL Server 首选项中将星期配置为从星期日开始时,该代码才有效。这是大多数英语安装的默认设置(在这种情况下,SELECT @@DATEFIRST 将返回 7)

        【讨论】:

        • 1900 - 不是一个“神奇”的年份。事实上,这是大多数计算系统基于其日期结构的年份。
        • @JamieMarshall epoch 不清楚 大多数 计算系统将 1900 年视为时间的开始。您能否为这一事实提供引用?
        • @HABO - 很棒的维基链接。我的立场是正确的。
        • 但是 SQL Server 的纪元日期具体是 1900 年 1 月 1 日。所以虽然我的措辞在技术上是错误的,但我认为这里的解决方案仍然是不必要的复杂,因为该日期将始终有效。跨度>
        • @JamieMarshall 什么定义了 SQL Server 的时代?是的,SQL Server 的date/time data types 的基准日期是 1900-01-01。还有 0001-01-01 和 1753-01-01。然后是two digit year cutoff configuration 来增强导入日期字符串的乐趣。 ...
        【解决方案7】:

        我需要按周对一些数据进行分组并显示每周的第一天(在我的例子中是星期日)。我使用以下方法计算一年中的第一个星期日,然后您可以使用周数来获取给定日期发生的一周的开始。

        DECLARE @firstSundayThisYear DATE,
            @jan1ThisYear DATE,
            @currentYear INT;
        
        SELECT @jan1ThisYear = '01-jan-' + CAST(DATEPART(YEAR, GETDATE()) AS VARCHAR(4)),
            @currentYear = DATEPART(YEAR, GETDATE());
        
        SET @firstSundayThisYear = DATEADD(DAY, -DATEPART(weekday, @jan1ThisYear) + 1, @jan1ThisYear);
        
        PRINT @firstSundayThisYear
        

        注意:@firstSundayThisYear 实际上可能是去年的日期,但是当您乘以周数时,一切都会解决。

        【讨论】:

          【解决方案8】:

          我真的不知道为什么这里的答案很简单,真的:

              // You can try any year
              DECLARE @YEAR INT = 2021
              // First you get the first day of the year
              DECLARE @FIRST_DAY_OF_YEAR DATE = CAST(@YEAR AS VARCHAR(4)) + '-01-01'
              // Then you get its weekday
              DECLARE @FIRST_DAY_OF_YEAR_WEEKDAY INT = DATEPART(WEEKDAY,@FIRST_DAY_OF_YEAR)
              // Then it's just a CASE matter, where you must see if the actual first day of the year is itself a monday or a sunday. If it's a monday we just return the first day, if it's a sunday we just add 1, if it's anything else beyond that we just have to substract the weekday from 9 (which is essentialy the second monday of the year). And we add the result to whichever day of the week is our first day of the year and we always get our first monday
              DECLARE @FIRST_MONDAY_OF_YEAR DATE =
              CASE
                  WHEN @FIRST_DAY_OF_YEAR_WEEKDAY = 2 THEN @FIRST_DAY_OF_YEAR
                  WHEN @FIRST_DAY_OF_YEAR_WEEKDAY = 1 THEN DATEADD(DAY, 1, @FIRST_DAY_OF_YEAR)
                  ELSE DATEADD(DAY, 9 - @FIRST_DAY_OF_YEAR_WEEKDAY, @FIRST_DAY_OF_YEAR)
              END
          
              SELECT @FIRST_MONDAY_OF_YEAR
          

          【讨论】:

            【解决方案9】:

            我知道这是一个 8 年前的问题,但我想我还是会把帽子扔进擂台

            下面的第一个 sn-p 创建一个测试表来证明后面的代码不会遇到任何 RBAR 问题。解决方案实际上是在第二个 sn-p 的结果中填充“FirstMondayOfYear”列的相当短的公式。其他一切都只是为了添加到演示中。不需要单独的 SET、CASE 语句和对 DATEFIRST 的引用。

            --===== Create a test table that contains random dates and times from 1900-01-01 up to and not
                 -- including 2100-01-01.
               DROP TABLE IF EXISTS #MyHead
            ;
             SELECT TOP 100000
                    SomeDateTime = RAND(CHECKSUM(NEWID()))*DATEDIFF(dd,'1900','2100')+CONVERT(DATETIME,'1900') --Inherently DATETIME
            --        SomeDateTime = CONVERT(DATETIME2(7),RAND(CHECKSUM(NEWID()))*DATEDIFF(dd,'1900','2100')+CONVERT(DATETIME,'1900')) --DATETIME2()
            --        SomeDateTime = CONVERT(DATE,ABS(CHECKSUM(NEWID())%DATEDIFF(dd,'1900','2100'))+CONVERT(DATETIME,'1900')) --DATE
               INTO #MyHead
               FROM      sys.all_columns ac1
              CROSS JOIN sys.all_columns ac2
            ;
            --===== Return the first Monday for the year of each given date (and the original date) as well
                 -- as a weekday name and day check for the date created by the formula.  The FirstMondayOfYear
                 -- should (obviously) always be a Monday and it should always be a day of 7 or less.
               WITH cteFindMonday AS
            (
             SELECT  SomeDateTime
                    ,FirstMondayOfYear  = DATEADD(dd,(DATEDIFF(dd,'1753',DATENAME(yy,SomeDateTime))+6)/7*7,'1753')
               FROM #MyHead
            )
             SELECT  SomeDateTime
                    ,FirstMondayOfYear
                    ,DoW     = DATENAME(dw,FirstMondayOfYear)
                    ,IsValid =  IIF(    DATENAME(dw,FirstMondayOfYear) = 'Monday'
                                    AND DATEPART(dd,FirstMondayOfYear) <= 7 
                                ,1,0)
               FROM cteFindMonday
              ORDER BY SomeDateTime
            ;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-05-15
              • 1970-01-01
              • 1970-01-01
              • 2016-11-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多