【问题标题】:Get only Saturday and Sunday dates of month in SQL Server在 SQL Server 中仅获取每月的周六和周日日期
【发布时间】:2021-02-02 18:19:23
【问题描述】:

我能否找到特定月份的星期六和星期日所在的日期?例如,考虑 2017 年 1 月的月份。以下日期为周末:

 7/1/2017 - Saturday
14/1/2017 - Saturday
21/1/2017 - Saturday
28/1/2017 - Saturday 
 1/1/2017 - Sunday
 8/1/2017 - Sunday    
15/1/2017 - Sunday
22/1/2017 - Sunday
29/1/2017 - Sunday

我想要一个 SQL Server 查询,这样当我将月份和年份作为输入传递时,我应该返回所有上述日期(仅限周六和周日的日期)作为输出

我不希望使用任何用户定义的函数并希望在单个 SELECT 语句中完成它

【问题讨论】:

    标签: sql-server


    【解决方案1】:

    注意:正如 cmets 中的另一位用户已经指出的,此查询取决于您的服务器设置,即 DATEFIRST。如果您因为设置不同需要更改查询,请告诉我,我可以为您更改。

    使用 CTE 作为虚拟数据...

    /* Ignore this part...*/
    WITH CTE AS
    (
        SELECT CAST('01/01/2017' AS DATE) AS [Date]
            UNION ALL
        SELECT DATEADD(DAY,1,[Date])
        FROM CTE
        WHERE DATE <= '12/31/2017'
    )
    /*Your actual SELECT statement would look like this, from your own table of course*/
    SELECT 
         [Date]
        ,CASE DATEPART(dw,[Date])
            WHEN 1 THEN 'Sunday'
            WHEN 2 THEN 'Monday'
            WHEN 3 THEN 'Tuesday'
            WHEN 4 THEN 'Wednesday'
            WHEN 5 THEN 'Thursday'
            WHEN 6 THEN 'Friday'
            WHEN 7 THEN 'Saturday'
        END
    FROM CTE
    WHERE DATEPART(dw,[Date]) IN (1,7)
    AND MONTH([Date]) = 12--<month>
    AND YEAR([Date]) = 2017--<year>
    OPTION (MAXRECURSION 0) -- You won't need this line if you're querying a real table
    ;
    

    如果运行它对你有用,那么你的真实查询可能看起来像这样:

    SELECT 
         [Date]
        ,CASE DATEPART(dw,[Date])
            WHEN 1 THEN 'Sunday'
            WHEN 2 THEN 'Monday'
            WHEN 3 THEN 'Tuesday'
            WHEN 4 THEN 'Wednesday'
            WHEN 5 THEN 'Thursday'
            WHEN 6 THEN 'Friday'
            WHEN 7 THEN 'Saturday'
        END
    FROM < the table you want >
    WHERE DATEPART(dw,[Date]) IN (1,7) -- Only Sundays and Saturdays
    AND MONTH([Date]) = < the month you want >
    AND YEAR([Date]) = < the year you want >
    ;
    

    如果您想生成数据,那么 CTE 就是要走的路。如果你传递参数,它看起来像这样:

    DECLARE
         @MONTH INT
        ,@YEAR INT
    ;
    
    SET @MONTH = 1;
    SET @YEAR = 2017;
    
    WITH CTE AS
    (
        SELECT CAST(CAST(@MONTH AS VARCHAR(2)) + '/01/' + CAST(@YEAR AS VARCHAR(4)) AS [Date]) AS DATE
            UNION ALL
        SELECT DATEADD(DAY,1,[Date])
        FROM CTE
        WHERE DATE <= CAST(@MONTH AS VARCHAR(2)) + 
            CASE 
                WHEN @MONTH IN (9,4,6,11) 
                    THEN '/30/'
                WHEN @MONTH IN (1,3,5,7,8,10,12) 
                    THEN '/31/'
                WHEN @MONTH = 2 AND @YEAR/4.00 = @YEAR/4 
                    THEN '/29/'
                ELSE '/28/'
            END
            + CAST(@YEAR AS VARCHAR(4))
    )
    SELECT 
         [Date]
        ,CASE DATEPART(dw,[Date])
            WHEN 1 THEN 'Sunday'
            WHEN 2 THEN 'Monday'
            WHEN 3 THEN 'Tuesday'
            WHEN 4 THEN 'Wednesday'
            WHEN 5 THEN 'Thursday'
            WHEN 6 THEN 'Friday'
            WHEN 7 THEN 'Saturday'
        END
    FROM CTE
    WHERE DATEPART(dw,[Date]) IN (1,7)
    OPTION (MAXRECURSION 0)
    ;
    

    【讨论】:

    • 此解决方案依赖于您的服务器设置,它仅适用于 DATEFIRST 7。如果您更改 datefirst SET DATEFIRST 1; 它将给出不同的结果。
    • 这完全正确,我会将其添加到我的帖子中。这正是我提供原始查询并在帖子中进一步询问用户结果是否符合他的喜好的原因;如果他回应说这给出了您所描述的不希望的结果,我会简单地更新查询以包含他的服务器逻辑 - 我无法提前知道:) 非常感谢您的建设性补充!
    【解决方案2】:

    请试试这个。

    DECLARE @Year AS INT=2017,
    @Month AS INT=3,
    @FirstDateOfYear DATETIME,
    @LastDateOfYear DATETIME
    
    SELECT @FirstDateOfYear = DATEADD(yyyy, @Year - 1900, 0)
    SELECT @LastDateOfYear = DATEADD(yyyy, @Year - 1900 + 1, 0)
    -- Creating Query to Prepare Year Data
    ;WITH cte AS (
    SELECT 1 AS DayID,
    @FirstDateOfYear AS FromDate,
    DATENAME(dw, @FirstDateOfYear) AS Dayname
    UNION ALL
    SELECT cte.DayID + 1 AS DayID,
    DATEADD(d, 1 ,cte.FromDate),
    DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
    FROM cte
    WHERE DATEADD(d,1,cte.FromDate) < @LastDateOfYear
    )
    SELECT FromDate AS Date, Dayname
    FROM CTE
    WHERE DayName IN ('Saturday','Sunday') and month(FromDate) = @Month 
    OPTION (MaxRecursion 370)
    

    【讨论】:

    • 我认为如果您将源链接添加到您的答案中没有错。目前看来您编写了此脚本...
    • @Husen : 这个查询选择了全年的所有日期,只在 where 子句中过滤了该月的周六和周日。
    【解决方案3】:

    这应该可以解决问题:

    DECLARE @month date = '2017-01-01'
    
    SET @month = dateadd(month, datediff(month, 0, @month), 0)
    
    ;WITH CTE as
    (
      SELECT 0 x
      FROM (values(1),(1),(1),(1),(1),(1)) x(n)
    ),
    CTE2 as
    (
      SELECT 
        top(day(eomonth(@month))) 
        -- use this syntax for sqlserver 2008
        -- top(datediff(d, @month,dateadd(month,1,@month))) 
        cast(dateadd(d, row_number()over(order by(select 1))-1,@month) as date) cDate
      FROM CTE CROSS JOIN CTE C2
    )
    SELECT
      cDate, 
      datename(weekday, cDate) Weekday
    FROM CTE2
    WHERE 
      datediff(d,0,cDate)%7 > 4
    

    Fiddle

    【讨论】:

    • EOMONTH() 的精彩使用。忘记了这个功能!赞成。
    • 我真的很想知道为什么这被否决了。据我所知,这是迄今为止性能和逻辑方面的最佳答案。甚至比从 sql 权限复制的递归解决方案要好得多。
    • 我当然不能告诉你。我只有几年的 SQL 经验就做出了我的回应,因此给出的响应时间比你长得多,效率低得多。你的简洁高效,但可能更难理解?我很想解释一下这个坏男孩是如何工作的……我认为这真的令人印象深刻。
    【解决方案4】:

    来自https://www.sqlservercentral.com/articles/finding-the-correct-weekday-regardless-of-datefirst,您只需:

    (DATEPART(dw, @your_date) + @@DATEFIRST) % 7 不在 2 和 6 之间

    【讨论】:

      【解决方案5】:

      根据要求,单个select,语言中立,dateFirst 中立,几乎 SQL 版本中立:

      declare @OneDate datetime = '28/01/2017'; -- Any date from the target month/year
      
      select MyDate -- raw date or ...
          -- convert(varchar, MyDate, 103) + ' - ' + dateName(dw, MyDate) -- as Sample
          as WeekEndDate
      from (
          select dateAdd(dd, number, dateAdd(mm, dateDiff(mm, 0, @OneDate), 0)) as MyDate
          from master..spt_values
          where type = 'P' and number < 31
          ) j
      where 1 + (datePart(dw, MyDate) + @@DATEFIRST + 5) % 7 in (6, 7)
          and month(MyDate) = month(@OneDate)
      -- order by 1 + (datePart(dw, MyDate) + @@DATEFIRST + 5) % 7, MyDate -- as Sample
      ;
      

      【讨论】:

        【解决方案6】:

        解决此问题的另一种方法如下-

        DECLARE @MONTH INT,@YEAR INT
        SET @MONTH = 1;
        SET @YEAR = 2017;
        
        Declare @StartDate date =CAST(CAST(@MONTH AS VARCHAR(2)) + '/01/' + CAST(@YEAR AS VARCHAR(4)) AS [Date]), @EndDate date
        Set @EndDate = EOMONTH(@StartDate)
        
        Declare @Temp table (DateOfDay date, DaysName varchar(50))
        While(@StartDate <= @EndDate)
        Begin
        
            Insert into @Temp
            SELECT  @StartDate DateOfMonth, 
                    case when DATENAME(DW, @StartDate) = 'Saturday' then DATENAME(DW, @StartDate) 
                         when DATENAME(DW, @StartDate) = 'Sunday' then DATENAME(DW, @StartDate) 
                    end DaysName
        
            set @StartDate = DATEADD(d,1,@StartDate)
        End
        
        select * from @Temp where DaysName is not null order by DaysName, DateOfDay
        

        【讨论】:

          【解决方案7】:

          你不能这样做吗?

          SELECT DATENAME(dw,'10/11/2016') AS DATE 
          WHERE DATE CONTAINS('Saturday') OR DATE CONTAINS('SUNDAY')
          

          而不是“2016 年 10 月 11 日”,您只需要弄清楚如何生成月/年的所有日期?

          【讨论】:

            最近更新 更多