每个月的第一个SQL

chu*_*lor 6 sql sql-server

假设我想在SQL中编写表值函数,它返回一个表,每个月的第一天在参数日期之间,最简单的方法是什么?

例如,fnFirstOfMonths('10/31/10', '2/17/11')将返回带有的单列表11/1/10, 12/1/10, 1/1/11,并2/1/11作为元素.

我的第一直觉只是使用while循环并重复插入几个月的前几天,直到我到达开始日期之前.看起来应该有更优雅的方式来做到这一点.

感谢您的任何帮助,您可以提供.

mar*_*c_s 5

这样的东西可以在不在函数内部的情况下工作:

DECLARE @LowerDate DATE 
SET @LowerDate = GETDATE()

DECLARE @UpperLimit DATE
SET @UpperLimit = '20111231'

;WITH Firsts AS
(
    SELECT
        DATEADD(DAY, -1 * DAY(@LowerDate) + 1, @LowerDate) AS 'FirstOfMonth'

    UNION ALL

    SELECT
        DATEADD(MONTH, 1, f.FirstOfMonth) AS 'FirstOfMonth'
    FROM
        Firsts f
    WHERE
        DATEADD(MONTH, 1, f.FirstOfMonth)  <= @UpperLimit
)   
SELECT * 
FROM Firsts
Run Code Online (Sandbox Code Playgroud)

它使用一种称为CTE(公用表表达式)的东西 - 可在SQL Server 2005及更高版本和其他数据库系统中使用.

在这种情况下,我通过确定@LowerDate指定日期的第一个月开始递归CTE ,然后迭代添加一个月到前一个月的第一个月,直到达到上限.

或者,如果您想将其打包在存储函数中,您也可以这样做:

CREATE FUNCTION dbo.GetFirstOfMonth(@LowerLimit DATE, @UpperLimit DATE)
RETURNS TABLE 
AS 
   RETURN
      WITH Firsts AS
      (
          SELECT
             DATEADD(DAY, -1 * DAY(@LowerLimit) + 1, @LowerLimit) AS 'FirstOfMonth'
          UNION ALL
          SELECT
             DATEADD(MONTH, 1, f.FirstOfMonth) AS 'FirstOfMonth'
          FROM
             Firsts f
          WHERE
             DATEADD(MONTH, 1, f.FirstOfMonth)  <= @UpperLimit
       )    
       SELECT * FROM Firsts
Run Code Online (Sandbox Code Playgroud)

然后像这样调用它:

SELECT * FROM dbo.GetFirstOfMonth('20100522', '20100831')
Run Code Online (Sandbox Code Playgroud)

获得这样的输出:

FirstOfMonth
2010-05-01
2010-06-01
2010-07-01
2010-08-01
Run Code Online (Sandbox Code Playgroud)

PS:通过使用DATESQL Server 2008及更新版本中的数据类型 - 我修复了Richard评论的两个"错误".如果您使用的是SQL Server 2005,则必须使用DATETIME- 并处理您正在获得时间部分的事实.