【问题标题】:How can i get the First Date and Last Date between selected months?如何获取所选月份之间的第一个日期和最后一个日期?
【发布时间】:2016-10-10 04:59:56
【问题描述】:

如何获取所选月份之间的第一个日期和最后一个日期?

例如我输入年份 2015,月份 09 到 2016 08

返回值必须是:

StartDate EndDate   
20150901 20150930  
20151001 20151031  
20151101 20151130  
20151201 20151231  
20160101 20160131  
20160201 20160229  
20160301 20160331   
20160401 20160430      
20160501 20160531   
20160601 20160630   
20160701 20160731   
20160801 20160831   

我发现多个查询执行相同但仅针对 1 个插入日期。例如:

DECLARE @mydate DATETIME
SELECT @mydate = GETDATE()
SELECT CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(@mydate)-1),@mydate),101) AS Date_Value,
'First Day of Current Month' AS Date_Type
UNION

SELECT CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,@mydate))),DATEADD(mm,1,@mydate)),101) ,
'Last Day of Current Month'

但我找不到可以为 Between 语句执行此操作的语句

【问题讨论】:

标签: sql sql-server sql-server-2008 date


【解决方案1】:

我有两个 udf,可以单独使用或结合使用。他们动态地创建具有可变日期部分和/或增量的日期范围。

Select * from [dbo].[udf-Create-Range-Date-Span]('2015-09-01','2016-08-01','MM',1) 

DateR1                  DateR2
2015-09-01 00:00:00.000 2015-10-01 00:00:00.000
2015-10-01 00:00:00.000 2015-11-01 00:00:00.000
2015-11-01 00:00:00.000 2015-12-01 00:00:00.000
2015-12-01 00:00:00.000 2016-01-01 00:00:00.000
2016-01-01 00:00:00.000 2016-02-01 00:00:00.000
2016-02-01 00:00:00.000 2016-03-01 00:00:00.000
2016-03-01 00:00:00.000 2016-04-01 00:00:00.000
2016-04-01 00:00:00.000 2016-05-01 00:00:00.000
2016-05-01 00:00:00.000 2016-06-01 00:00:00.000
2016-06-01 00:00:00.000 2016-07-01 00:00:00.000
2016-07-01 00:00:00.000 2016-08-01 00:00:00.000

现在,我专门使用上限,以便可以查询 DateR1 和 DateR2 之间的数据(包括时间)和

不过,稍加改动,您就可以更改输出。例如:

Select DateR1=cast(DateR1 as Date),DateR2=DateAdd(DD,-1,cast(DateR2 as Date)) from [dbo].[udf-Create-Range-Date-Span]('2015-09-01','2016-08-01','MM',1) 

DateR1      DateR2
2015-09-01  2015-09-30
2015-10-01  2015-10-31
2015-11-01  2015-11-30
2015-12-01  2015-12-31
2016-01-01  2016-01-31
2016-02-01  2016-02-29
2016-03-01  2016-03-31
2016-04-01  2016-04-30
2016-05-01  2016-05-31
2016-06-01  2016-06-30
2016-07-01  2016-07-31

两个函数如下:

CREATE FUNCTION [dbo].[udf-Create-Range-Date] (@DateFrom datetime,@DateTo datetime,@DatePart varchar(10),@Incr int)

Returns 
@ReturnVal Table (RetVal datetime)

As
Begin
    With DateTable As (
        Select DateFrom = @DateFrom
        Union All
        Select Case @DatePart
               When 'YY' then DateAdd(YY, @Incr, df.dateFrom)
               When 'QQ' then DateAdd(QQ, @Incr, df.dateFrom)
               When 'MM' then DateAdd(MM, @Incr, df.dateFrom)
               When 'WK' then DateAdd(WK, @Incr, df.dateFrom)
               When 'DD' then DateAdd(DD, @Incr, df.dateFrom)
               When 'HH' then DateAdd(HH, @Incr, df.dateFrom)
               When 'MI' then DateAdd(MI, @Incr, df.dateFrom)
               When 'SS' then DateAdd(SS, @Incr, df.dateFrom)
               End
        From DateTable DF
        Where DF.DateFrom < @DateTo
    )

    Insert into @ReturnVal(RetVal) Select DateFrom From DateTable option (maxrecursion 32767)

    Return
End

CREATE FUNCTION [dbo].[udf-Create-Range-Date-Span] (@Date1 datetime,@Date2 datetime,@DatePart varchar(10),@Incr int)

-- Syntax Select * from [dbo].[udf-Create-Range-Date-Span]('2016-10-01','2020-10-01','YY',1) 
-- Syntax Select * from [dbo].[udf-Create-Range-Date-Span]('2016-01-01','2016-12-31','MM',1) 
-- Syntax Select * from [dbo].[udf-Create-Range-Date-Span]('2016-10-01','2016-10-31','MI',15) 
-- Syntax Select * from [dbo].[udf-Create-Range-Date-Span]('2016-10-01','2016-10-02','SS',1) 

Returns Table 

As
Return (

     Select DateR1 = RetVal
           ,DateR2 = LEAD(RetVal,1,@Date2) OVER (ORDER BY RetVal)
     From (Select * from [dbo].[udf-Create-Range-Date](@Date1,@Date2,@DatePart,@Incr) ) A
     Where RetVal<@Date2

)

【讨论】:

    【解决方案2】:

    你不需要UDFs ——你可以用一个查询来做到这一点:

    Declare @FromYear   Int = 2015,
            @FromMonth  Int = 9,
            @ToYear     Int = 2016,
            @ToMonth    Int = 8
    
    Declare @FromDate   Date = (Select DateAdd(Year, @FromYear - 1900, DateAdd(Month,  @FromMonth - 1, 0))),
            @ToDate     Date = (Select DateAdd(Year, @ToYear - 1900, DateAdd(Month, @ToMonth - 1, 0)))
    
    ;With Date (Date) As
    (
        Select  @FromDate Union All
        Select  DateAdd(Month, 1, Date)
        From    Date
        Where   Date < @ToDate
    )
    Select  Convert(Varchar, Year(Date)) + Right('00' + Convert(Varchar, Month(Date)), 2) + Right('00' + Convert(Varchar, Day(Date)), 2)    As StartDate,
            Convert(Varchar, Year(Date)) + Right('00' + Convert(Varchar, Month(Date)), 2) + Right('00' + Convert(Varchar, Day(DateAdd(Day, -1, DateAdd(Month, DateDiff(Month, 0, Date) + 1, 0)))), 2)   As EndDate 
    From    Date
    Option  (MaxRecursion 0)
    

    输出

    StartDate   EndDate
    20150901    20150930
    20151001    20151031
    20151101    20151130
    20151201    20151231
    20160101    20160131
    20160201    20160229
    20160301    20160331
    20160401    20160430
    20160501    20160531
    20160601    20160630
    20160701    20160731
    20160801    20160831
    

    但是,如果您能够迁移到 SQL Server 2012 或更高版本,则查询会简单得多:

    Declare @FromYear   Int = 2015,
            @FromMonth  Int = 9,
            @ToYear     Int = 2016,
            @ToMonth    Int = 8
    
    Declare @FromDate   Date    = DateFromParts(@FromYear, @FromMonth, 1),
            @ToDate     Date    = DateFromParts(@ToYear, @ToMonth, 1)
    
    ;With Date (Date) As
    (
        Select  @FromDate Union All
        Select  DateAdd(Month, 1, Date)
        From    Date
        Where   Date < @ToDate
    )
    Select  Format(Date, N'yyyyMMdd')           As StartDate, 
            Format(EoMonth(Date), N'yyyyMMdd')  As EndDate
    From    Date
    Option  (MaxRecursion 0)
    

    【讨论】:

    • 我不同意。 UDF 非常灵活,可以从众多查询中调用。简而言之,它们为多个主服务器提供服务。
    • @JohnCappelletti 不反对 UDF,它们非常实用。我只是说您不需要需要拥有它们(例如,它们不是必需的)。有时最好让所有东西都自给自足。不过,这主要是情景和个人喜好:)
    • 同意,有时最好让每个人都自给自足。也就是说,我是一个懒惰的人。我宁愿做某事一次,而不必再考虑。干杯。
    【解决方案3】:

    试试这个。它也是一个非常小的脚本。我喜欢 Siyual 的 CTE 想法。但这要短得多。

    DECLARE @inputYear INT = 2015,
    @inputMonth INT = 09
    
    SELECT DATEADD(MONTH, @inputMonth-1, DATEADD(YEAR, @inputYear-1900, 0)) AS FirstDayOfTheMonth,
    DATEADD(s,-1, DATEADD(MONTH, @inputMonth, DATEADD(YEAR, @inputYear-1900, 0))) AS LastDayOfTheMonth 
    

    【讨论】:

      【解决方案4】:

      您的任务也可以使用单个 ANSI sql 语句来完成。 但是,您需要两个表 ALL_YEARS 和 ALL_MONTHS,其中包含使用的年份和月份集。

      例如如果考虑 1900 到 2099 之间的可能年份,则表 ALL_YEARS 必须包含 1900 到 2099 之间的数字。 表 ALL_MONTHS 必须包含 1 到 12 之间的数字。

      使用这两个表,语句如下:

      select            to_date('01.' || to_char(all_months.month_value) || '.' || to_char(all_years.year_value ), 'dd.mm.yyyy') first_day ,
             add_months(to_date('01.' || to_char(all_months.month_value) || '.' || to_char(all_years.year_value ), 'dd.mm.yyyy'), 1) -1 last_day
      from   all_years,
             all_months
      where  to_date('01.' || to_char(all_months.month_value) || '.' || to_char(all_years.year_value ), 'dd.mm.yyyy') between 
              to_date('01.02.2014', 'dd.mm.yyyy') and 
              to_date('01.07.2016', 'dd.mm.yyyy');
      

      输出如下:

      01.02.14    28.02.14
      01.03.14    31.03.14
      01.04.14    30.04.14
      01.05.14    31.05.14
      01.06.14    30.06.14
      ...
      01.06.16    30.06.16
      01.07.16    31.07.16
      

      【讨论】:

        猜你喜欢
        • 2021-12-07
        • 2012-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多