【问题标题】:Create a year/quarter table in SQL without a loop在没有循环的 SQL 中创建年/季度表
【发布时间】:2019-02-08 04:14:50
【问题描述】:

我有一个开始年份和一个结束年份,例如 2017 年和 2019 年。

我想在我声明的 startYear 和 endYear 之间创建一个包含 yearQuarter 列(例如 1、2、3、4)的表格,并且有季度,用于决赛,endYear,在 2 点停止(它总是向前看的)。

下面的示例所需输出。

year    quarter
2017    1
2017    2
2017    3
2017    4
2018    1
2018    2
2018    3
2018    4
2019    1
2019    2

看起来应该很简单,除了依赖循环或 UNION 的笨拙方法或简单地将值手动插入表中之外,我什么都没有想到。

【问题讨论】:

  • 为什么季度总是在最后一年的 2 之后停止?
  • 最后一年是一个前瞻性的缓冲...总是 [year run] + 1, 2 个季度就足够了。
  • 既然我确信你的用例会被扩展,为什么不把这个一次放到一个持久化的表中,这样你就不必再计算任何东西了。 Aaron Bertrand 为 check it out here 编写了一个很棒的脚本,而且速度超级快
  • @scsimon,只在更广泛的 SP 中使用过一次。 +1 链接虽然
  • 如果只有 2-3 年,您可以随时输入它们。

标签: sql sql-server tsql


【解决方案1】:

只是另一种选择...与Cross Join 配合使用的临时计数表

示例

Declare @Y1 int = 2017
Declare @Y2 int = 2019

Select *
 From ( Select Top (@Y2-@Y1+1) Year=@Y1-1+Row_Number() Over (Order By (Select NULL)) From master..spt_values n1 ) A
 Cross Join (values (1),(2),(3),(4)) B([Quarter])

退货

Year    Quarter
2017    1
2017    2
2017    3
2017    4
2018    1
2018    2
2018    3
2018    4
2019    1
2019    2
2019    3
2019    4

【讨论】:

  • 更好,因为在后端,递归 CTE 也可以被视为循环。
  • 有趣...看来您正在为季度创建一个临时表格。如果我理解,'B' 是别名,'Quarter' 是列名...您能否链接到文档以了解如何创建这样的“表”?
  • @Chris,您可以试试这个SELECT * FROM (VALUES(1),(2),(3))t(x) 并阅读this。约翰,我这边 +1
【解决方案2】:

使用递归 CTE:

with yq as (
      select 2017 as yyyy, 1 as qq
      union all
      select (case when qq = 4 then yyyy + 1 else yyyy end), 
              (case when qq = 4 then 1 else qq + 1 end)
      from yq
      where yyyy < 2019 or yyyy = 2019 and qq < 2
     )
select *
from yq;

如果表格超过 100 行,您还需要option (maxrecursion 0)

Here 是一个 dbfiddle。

【讨论】:

  • 我们不应该将 where 条件写为 -- where yyyy
  • @Namandeep_Kaur 。 . .首先,你有没有看到 dbfiddle。该代码有效。其次,您可以使用括号。此逻辑不需要它们。第三,qq &lt; 2 是正确的。这将以值 2 结束。
  • 我的错!应该检查 dbfiddle.
【解决方案3】:

此解决方案与 John 的解决方案非常相似,但它不依赖于系统表。

Declare @Y1 int = 2017;
Declare @Y2 int = 2019;

WITH 
E(n) AS(
    SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)
),
E2(n) AS(
    SELECT a.n FROM E a, E b
),
E4(n) AS(
    SELECT a.n FROM E2 a, E2 b
),
cteYears([Year]) AS(
    SELECT TOP (@Y2-@Y1+1)
            ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + @Y1 - 1 AS [Year]
    FROM E4
)
SELECT [Year], [Quarter]
FROM cteYears
CROSS JOIN (VALUES (1),(2),(3),(4)) Q([Quarter]);

【讨论】:

  • 嘿路易斯。很高兴在这里看到另一个好人。 :)
  • 只是为了清楚。任何足够大小的表都可以,请注意 Order By
【解决方案4】:

让我为您提出一个递归查询:

WITH prepare AS
(
SELECT tbl.year
FROM (VALUES (2017) ) AS tbl(year)    -- for example, start year is 2k17

UNION ALL

SELECT year + 1
FROM prepare
WHERE year < 2030                     -- and last year is 2030
)
SELECT
    year, quarter
FROM prepare
CROSS JOIN ( VALUES (1), (2), (3), (4) ) AS tbl (quarter)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 2018-12-20
    • 2013-12-02
    • 1970-01-01
    相关资源
    最近更新 更多