【问题标题】:SQL Split Period by Months按月划分 SQL 周期
【发布时间】:2015-11-03 22:13:03
【问题描述】:

下午好, 我有一个看似简单的问题,但事实证明并不那么简单。我有 2 个约会。 BeginPeriod (2010-06-10) 和 EndPeriod (2011-06-11)。

我想看看这些日期是否可以分解为各自的每月细分。对于上面的例子,类似于

  • 2010/06/10 - 2010/06/30
  • 2010/07/01 - 2010/07/31
  • 2010/08/01 - 2010/08/31
  • …………
  • 2011/06/01 - 2011/06/10

我对方法并不特别。 CTE 很好,但不是首选。正如他们所说,乞丐不能挑剔。

一切顺利, 乔治

【问题讨论】:

  • 您在尝试实现此代码时遇到了哪些问题?

标签: sql sql-server tsql common-table-expression


【解决方案1】:

它是 CTE。

DECLARE @BeginPeriod DATETIME = '2010-06-10',
        @EndPeriod DATETIME = '2011-06-11'

;WITH cte AS
(
    SELECT DATEADD(month, DATEDIFF(month, 0, @BeginPeriod), 0) AS StartOfMonth, 
           DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, @BeginPeriod) + 1, 0)) AS EndOfMonth
    UNION ALL
    SELECT DATEADD(month, 1, StartOfMonth) AS StartOfMonth, 
           DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, DATEADD(month, 1, StartOfMonth)) + 1, 0)) AS EndOfMonth  
    FROM   cte
    WHERE  DATEADD(month, 1, StartOfMonth) <= @EndPeriod
)
SELECT  
    (CASE WHEN StartOfMonth < @BeginPeriod THEN @BeginPeriod ELSE StartOfMonth END) StartOfMonth,
    (CASE WHEN EndOfMonth > @EndPeriod THEN @EndPeriod ELSE EndOfMonth END) EndOfMonth
FROM cte

最后一个EndOfMonth 是您用作@EndPeriod 的值,如果您想要前一天,请将其设置为DATEADD(day, -1, @EndPeriod)

你可以用它来修剪时间。

SELECT  
    CONVERT(VARCHAR(10), (CASE WHEN StartOfMonth < @BeginPeriod THEN @BeginPeriod ELSE StartOfMonth END), 120) StartOfMonth,
    CONVERT(VARCHAR(10), (CASE WHEN EndOfMonth > @EndPeriod THEN @EndPeriod ELSE EndOfMonth END), 120) EndOfMonth
FROM cte

【讨论】:

  • 感谢您的正确回答。对此,我真的非常感激。它就像一个魅力。
  • 感谢您提醒我有关递归 CTE,帮助我在另一个类似问题上获得了一些声誉 :)
【解决方案2】:

“数字”CTE 的另一个选项

声明@df 日期时间,@dt 日期时间 设置@df = '20100610' 设置@dt = '20110611' ;与 e1(n) 作为 ( 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 联合全部 选择 1 ), -- 10 e2(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY e1.n) FROM e1 CROSS JOIN e1 AS b) -- 10*10 选择 e2.n = 1 的情况 然后@df 否则 dateadd(day, -day(@df) + 1, dateadd(month, e2.n - 1, @df)) 结束, e2.n = datediff(month, @df, @dt) + 1 的情况 然后 dateadd(month, e2.n -1 , @df) 否则 EOMONTH( dateadd(month, e2.n -1 , @df) ) 结束 从 e2 其中 e2.n

您可以使用其他选项代替 Numbers CTE,例如此处所述 http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1 我的数据库中经常有 Numbers 表来执行此类任务。我认为主要是因为我在将 CTE 添加到 MS SQL 之前就开始使用这种技术,但它也减少了输入。

【讨论】:

    【解决方案3】:

    我知道这是一个老问题,但我最近才遇到同样的问题,并写了一个没有 CTE 的版本。

    这是经过测试的code

    
    DECLARE @FromDate DATETIME 
          , @ToDate DATETIME
          , @UpdateBeginDate BIT   
          , @UpdateEndDate BIT    
    
        SET @FromDate = '2019-01-15'
        SET @ToDate = '2021-08-17'
        SET @UpdateBeginDate = 1
        SET @UpdateEndDate = 1
    
    DECLARE @Results TABLE ( 
          MonthStart DATETIME
        , MonthEnd DATETIME 
        , TheMonth VARCHAR(2) 
        , TheYear VARCHAR(4)
    )
    
    
         -- Months in that period
         INSERT INTO @Results
         SELECT TOP (DATEDIFF(MONTH, @FromDate, @ToDate)+1) -- calculate how many rows needed
              DATEADD(MONTH, number, @FromDate)
            , DATEADD(MONTH, number, @FromDate)
            , MONTH(DATEADD(MONTH, number, @FromDate))
            , YEAR(DATEADD(MONTH, number, @FromDate))
           FROM [master].dbo.spt_values 
          WHERE [type] = N'P' 
          ORDER BY number
    
         /*
             by using [master].dbo.spt_values, the total series number are 2048, 
             for month cases, there will be 2048/12 ≈ 170 year range
         */
    
         -- Update first date of month and last date of month for each row
         UPDATE @Results
            SET MonthStart = DATEADD(MONTH, DATEDIFF(MONTH, 0, MonthStart), 0) 
              , MonthEnd = DATEADD(MONTH, ((YEAR(MonthEnd) - 1900) * 12) + MONTH(MonthEnd), -1)
         
         /*
             MonthStart
             Caculate the month difference from 1900-01-01 (0 represent 1900-01-01) to the current row's MonthStart record (DATEDIFF part)
             Then add the month difference from 1900-01-01 (DATEADD part)
    
             MonthEnd
             Caculate the year difference from 1900 to the current row's MonthEnd record then multiply 12 and plus the months of the record (The middle part)
             Then add the month difference from 1899-12-31 (DATEADD part)
             NOTE: -1 means 1899-12-31
    
             The MonthEnd sentence is equivalent to this, if you keeped the record of TheYear and TheMonth
             MonthEnd = DATEADD(MONTH, ((TheYear - 1900) * 12) + TheMonth, -1)
         */
    
    
         IF @UpdateBeginDate = 1
         BEGIN
             UPDATE @Results 
                SET MonthStart = @FromDate
              WHERE MonthStart = (SELECT MIN(MonthStart) FROM @Results)
         END
    
    
         IF @UpdateEndDate = 1
         BEGIN
             UPDATE @Results 
                SET MonthEnd = @ToDate
              WHERE MonthEnd = (SELECT MAX(MonthEnd) FROM @Results)
         END
    
    
        select * from @Results
    

    我在 MSSQL 2005 和 MSSQL 2017 中测试了这段代码,我认为其他版本也可能运行良好。

    希望这将帮助其他想要没有 CTE 的解决方案的人。

    【讨论】:

      猜你喜欢
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多