【问题标题】:TSQL generate months based on start and end dateSQL 根据开始和结束日期生成月份
【发布时间】:2018-08-23 01:12:04
【问题描述】:

我有两个变量 startend 包含日期值,例如2018-05-012019-02-28。我想创建一个包含其间每个月的表。结果表应如下所示。

Month   Year
5       2018
6       2018
7       2018
8       2018
9       2018
10      2018
11      2018
12      2018
01      2019
02      2019

如何做到这一点?

【问题讨论】:

  • SELECT DATEADD(MONTH, n, @start) FROM nums WHERE n BETWEEN 1 AND DATEDIFF(MONTH, @start, @end)nums 是一个合适的数字表(有些人更喜欢 CTE;搜索 SO 以获取有关如何制作的问题)。我可能会先走一步,用盐和胡椒调味,打开包装,得到DATETIME

标签: sql-server tsql date datetime


【解决方案1】:

这是我在 MS-SQL 中使用的一个表函数

CREATE FUNCTION [dbo].[GetSequencedMonthSplit](@StartDate DATETIME, @EndDate DATETIME)
RETURNS @Results TABLE 
(
    ID INT IDENTITY(1,1) 
    , YearValue INT 
    , MonthValue INT 
    , MonthName NVARCHAR(50) 

)
AS

BEGIN 



IF @StartDate IS NULL OR @EndDate IS NULL 
BEGIN 
    /*GET THE CURRENT CALENDAR YEAR*/
    SELECT 
        @StartDate = DATEFROMPARTS(year(getdate()),1,1)
        ,@EndDate = DATEFROMPARTS(year(getdate()),12,31)
END 



WHILE @StartDate < @EndDate 
BEGIN 
     INSERT INTO @Results (YearValue, MonthValue, MonthName) 
     SELECT 
        DATEPART(year, @StartDate)
        , DATEPART(month, @StartDate) 
        , DATENAME(month,@StartDate) 




    SET @StartDate = DATEADD(month, 1, @StartDate) 
END 


    RETURN  


END

然后调用这样的东西:

select 
    MonthValue AS [Month]
  , YearValue  as [Year]
from 
    dbo.GetSequencedMonthSplit(@StartDate,@EndDate)

应该给你你正在寻找的东西。

【讨论】:

    【解决方案2】:

    Jeroen Mostert 在您的 cmets 中的解决方案是最好的方法。 David 的解决方案将为您提供所需的内容,但他的函数是 Multi-statement 内联表值函数,您想要和 inline 表值函数 for the reasons outlined here。即使您只处理少数几行,多语句也会降低使用调用它们的查询的性能。

    要创建 inline 表值函数 (iTVF),您只需了解 tally tables 的工作原理。这样做会改变你的职业生涯。您正在寻找的 iTVF 版本如下所示:

    CREATE FUNCTION dbo.MonthYearRange (@startdate DATE, @enddate DATE)
    RETURNS TABLE WITH SCHEMABINDING AS RETURN
    WITH L1   AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) x(N)),
    iTally(N) AS
    ( SELECT 0 UNION ALL
      SELECT TOP (DATEDIFF(MONTH,@startdate,@enddate)) ROW_NUMBER() OVER (ORDER BY (SELECT 1))
      FROM L1 a CROSS JOIN L1 b CROSS JOIN L1 c)
    SELECT [Month] = MONTH(d.dt),
           [Year]  = YEAR(d.dt)
    FROM iTally i
    CROSS APPLY (VALUES (DATEADD(MONTH,i.N,@startdate))) d(dt);
    

    此查询:

    DECLARE @startdate DATE = '2018-05-01',
            @enddate   DATE = '2019-02-28';
    

    返回:

    Month       Year
    ----------- -----------
    5           2018
    6           2018
    7           2018
    8           2018
    9           2018
    10          2018
    11          2018
    12          2018
    1           2019
    2           2019
    

    【讨论】: