【问题标题】:For each quarter between two dates, add rows quarter by quarter in SQL SERVER对于两个日期之间的每个季度,在 SQL SERVER 中逐季度添加行
【发布时间】:2020-09-18 14:51:06
【问题描述】:

我有一个表格,类型为 int、datetime、datetime:

id    start date    end date
--    ----------    ----------
1     2019-04-02    2020-09-17
2     2019-08-10    2020-08-10

这里是创建/插入:

CREATE TABLE dbo.something
(
  id           int, 
  [start date] datetime, 
  [end date]   datetime
);

INSERT dbo.something(id,[start date],[end date]) 
  VALUES(1,'20190402','20200917'),(2,'20190810','20200810');

什么是可以产生这些结果的 SQL 查询:

id    Year    Quarter
--    ----    ----------
1     2019    2
1     2019    3
1     2019    4
1     2020    1
1     2020    2
1     2020    3
2     2019    3
2     2019    4
2     2020    1
2     2020    2
2     2020    3

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    只需使用递归 CTE。此版本从第 0 年切换到计算季度:

    with cte as (
          select id, 
                 year(start_date) * 4 +  datepart(quarter, start_date) - 1 as yyyyq,
                 year(end_date) * 4 + datepart(quarter, end_date) - 1 as end_yyyyq
          from t
          union all
          select id, yyyyq + 1, end_yyyyq
          from cte
          where yyyyq < end_yyyyq
         )
    select id, yyyyq / 4 as year, (yyyyq % 4) + 1 as quarter
    from cte;
    

    Here 是一个 dbfiddle。

    【讨论】:

    • 这正是我寻找的原因!谢谢!!
    【解决方案2】:

    如果您无法制作另一个参考表/等,您可以使用 DATEDIFF(和 DATEPART)使用季度,然后使用一些简单的日期算术。

    下面的逻辑只是为了找到每个开始日期的第一季度,然后再找到其他季度的数量以达到最大值。然后执行 SELECT,将其他季度添加到开始日期,以获取每个季度。

    查询中最难理解 imo 的部分是 WITH numberlist 部分 - 所有这些都是生成一系列介于 0 和最大四分之一差数之间的整数。如果您已经有一个数字表,则可以使用它。

    关键代码部分如下,这是完整的DB_Fiddle 以及一些额外的测试数据。

    CREATE TABLE #yourtable (id int, startdate date, enddate date)
    INSERT INTO #yourtable (id, startdate, enddate) VALUES
    (1, '2019-04-02', '2020-09-17'),
    (2, '2019-08-10', '2020-08-20')
    
    ; WITH number_list AS
            -- list of ints from 0 to maximum number of quarters
            (SELECT n 
                FROM (SELECT ones.n + 10*tens.n AS n
                        FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
                             (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n)
                    ) AS a
                WHERE n <= (SELECT MAX(DATEDIFF(quarter,startdate,enddate)) FROM #yourtable)
            )
        SELECT  id,
                YEAR(DATEADD(quarter, number_list.n, startdate)) AS [Year],
                DATEPART(quarter, DATEADD(quarter, number_list.n, startdate)) AS [Quarter]
        FROM    (SELECT id, startdate, DATEDIFF(quarter,startdate,enddate) AS num_additional_quarters FROM #yourtable) yt
                CROSS JOIN number_list
        WHERE   number_list.n <= yt.num_additional_quarters 
    
    
    DROP TABLE #yourtable
    

    【讨论】:

    • '2019-04-02' 是一种错误的文字格式,check what it returns under, say, SET LANGUAGE GERMAN;。使用'20190402' 更安全,那么 SQL Server 就不会误解。
    • 这太糟糕了@AaronBertrand - 感谢您指出这一点!我认为使用破折号是首选格式,但我学到了一些新东西。谢谢!
    • 确实如此。我在更多的文章和演讲中提到它,我记不得了。
    【解决方案3】:

    首先创建一个包含日期、对应季度和年份的日期维度表。然后使用下面的查询来获得结果。根据您的架构调整列和表名。

    with q_date as
    (
    select 1 as id, '2019-04-02' :: date as start_date, '2020-09-17' :: date as end_date
    UNION ALL
    select 2 as id, '2019-08-10' :: date as start_date, '2020-08-10' :: date as end_date
    )
    
    
    select qd.id, dd.calendar_year, dd.calendar_quarter_number 
    from dim_date dd, q_date qd 
    where dd.date_dmk between qd.start_date and qd.end_date
    group by qd.id, dd.calendar_year, dd.calendar_quarter_number
    order by qd.id, dd.calendar_year, dd.calendar_quarter_number;
    

    【讨论】:

    • 按 1,2,3 分组 ??
    • 这是替换列别名的懒惰快捷方式。您始终可以在 group by 中使用列名。
    • 很清楚,但是您在 SQL Server 中尝试过 GROUP BY 吗?
    • 即使它在 SQL Server 中是合法的,恕我直言,这将是一个糟糕的主意。 sqlblog.org/2009/10/06/bad-habits-to-kick-order-by-ordinal
    • 我想你也可以修复 '2019-04-02' :: date 这在 SQL Server 中是不合法的。
    猜你喜欢
    • 2015-05-05
    • 1970-01-01
    • 2022-06-18
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 2019-02-11
    • 1970-01-01
    • 2015-05-06
    相关资源
    最近更新 更多