SQL 到 SELECT 过去 4 个财政季度

SQL to SELECT last 4 Financial Quarters

我正在学习 SQL 并且我有一个看起来很可怕的查询 select 最近 4 financial/fiscal 个季度以及它们是多少个季度之前。

如果日期是 2018 年 3 月 28 日,则返回的 table 类似于:

╔═══╦════════════╦═════════════╗  
║   ║ ID         ║ QuartersAgo ║  
╠═══╬════════════╬═════════════╣  
║ 1 ║ 20182      ║ 1           ║  
║ 2 ║ 20181      ║ 2           ║  
║ 3 ║ 20174      ║ 3           ║  
║ 4 ║ 20173      ║ 4           ║  
╚═══╩════════════╩═════════════╝  

我的查询如下。当然有更好、更有效的方法……我喜欢它的原因是它可以工作并且我可以将它提交给版本控制(不像 Excel 和 PowerBI,我通常会在其中构建它)。

在第一阶段,我根据日历年的月份确定财政年度。

然后我以类似的方式将年份分解为季度以确定当前的财务季度。

一旦我发现我正在设置和确定第 n 个季度前的值。

我不喜欢的是,我觉得我应该在确定我属于哪个季度时将值插入变量 table。相反,我分配给一个值,然后稍后插入。

DECLARE @Today DATE = GETDATE();

DECLARE @ThisCalendarYear VARCHAR(4)
DECLARE @ThisFinancialYear VARCHAR(4)
DECLARE @ThisCalendarMonth INT

DECLARE @Last4FinancialQuarters TABLE(
    ID VARCHAR(5) NOT NULL,
    QuartersAgo INT NOT NULL
);

DECLARE @LastFinancialQuarter VARCHAR(5)
DECLARE @SecondLastFinancialQuarter VARCHAR(5)
DECLARE @ThirdLastFinancialQuarter VARCHAR(5)
DECLARE @FourthLastFinancialQuarter VARCHAR(5)

SET @ThisCalendarYear = DatePart(Year, @Today)
SET @ThisCalendarMonth = DatePart(Month, @Today)

IF DatePart(Month, @Today) <= 6
  SET @ThisFinancialYear = DatePart(Year, @Today)
ELSE 
  SET @ThisFinancialYear = DatePart(Year, @Today) +1 

SET @LastFinancialQuarter = CASE
  WHEN  @ThisCalendarMonth <= 3 THEN CONCAT(@ThisCalendarYear     ,4 - 2)
  WHEN  @ThisCalendarMonth <= 6 THEN CONCAT(@ThisCalendarYear     ,4 - 1)
  WHEN  @ThisCalendarMonth <= 9 THEN CONCAT(@ThisCalendarYear     ,4 - 0)
  WHEN  @ThisCalendarMonth <= 12 THEN CONCAT(@ThisCalendarYear + 1,4 - 3)
END

SET @SecondLastFinancialQuarter = CASE
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 4 THEN CONCAT(@ThisFinancialYear,3)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 3 THEN CONCAT(@ThisFinancialYear,2)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 2 THEN CONCAT(@ThisFinancialYear,1)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 4 THEN CONCAT(@ThisFinancialYear - 1,4)
END

SET @ThirdLastFinancialQuarter = CASE
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 4 THEN CONCAT(@ThisFinancialYear,2)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 3 THEN CONCAT(@ThisFinancialYear,1)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 2 THEN CONCAT(@ThisFinancialYear - 1,4)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 4 THEN CONCAT(@ThisFinancialYear - 1,3)
END

SET @FourthLastFinancialQuarter = CASE
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 4 THEN CONCAT(@ThisFinancialYear,1)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 3 THEN CONCAT(@ThisFinancialYear - 1,4)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 2 THEN CONCAT(@ThisFinancialYear - 1,3)
  WHEN SUBSTRING(@LastFinancialQuarter, 5,1) = 4 THEN CONCAT(@ThisFinancialYear - 1,2)
END


INSERT INTO @Last4FinancialQuarters (ID, QuartersAgo)
VALUES
    (@LastFinancialQuarter, 1),
    (@SecondLastFinancialQuarter, 2),
    (@ThirdLastFinancialQuarter, 3),
    (@FourthLastFinancialQuarter, 4);

SELECT * FROM @Last4FinancialQuarters

希望得到一些反馈 :) 谢谢

递归 CTE 来拯救:

;WITH cte AS
(
    SELECT 1 as QuartersAgo, GETDATE() as DT, 
        CAST(YEAR(DATEADD(MONTH, 3, GETDATE())) AS VARCHAR(4)) + CAST(DATEPART(QUARTER, DATEADD(MONTH, 3, GETDATE())) AS VARCHAR(1)) as FinancialQuarter
    UNION ALL 
    SELECT QuartersAgo + 1, DATEADD(MONTH, -3, cte.DT), 
        CAST(YEAR(DATEADD(MONTH, 3, DATEADD(MONTH, -3, cte.DT))) AS VARCHAR(4)) + CAST(DATEPART(QUARTER, DATEADD(MONTH, 3, DATEADD(MONTH, -3, cte.DT))) AS VARCHAR(1))
    FROM cte
    WHERE QuartersAgo < 4
)
SELECT FinancialQuarter, QuartersAgo FROM cte

这是输出:

FinancialQuarter    QuartersAgo
20182               1
20181               2
20174               3
20173               4

这里有一些要点:

  1. 要将今天的日期作为澳大利亚财政季度,请添加 3 个月并将年份连接到季度(您可以使用 DATEPART(QUARTER(DATE)) 获得),因此,如下所示:

    CAST(YEAR(DATEADD(MONTH, 3, GETDATE())) AS VARCHAR(4)) + CAST(DATEPART(QUARTER, DATEADD(MONTH, 3, GETDATE())) AS VARCHAR(1))

  2. CTE(常见 table 表达式)有点像临时 table 这在查询范围内。这是一个简单的解释。 CTE 值得一读!
  3. 您可以使用创建递归 CTE UNION ALL - UNION ALL 之前的查询部分是 锚点,递归部分在后面。在这种情况下,我使用 WHERE QuartersAgo < 4 停止查询在几个之后递归 工会。
  4. CTE 以 ;WITH 开头 - 所有 CTE 都以 "WITH" 开头, 并且分号只是终止所有挂起的乍得 悬在 CTE 定义前面。
  5. 括号后 围绕 CTE 定义,您可以从 CTE 查询。但只有 一次。之后,CTE 就超出了范围。
  6. 可以嵌套 一堆 CTE - CTE 可以引用它之前的 CTE,但不能 那些跟随它的人。

我也用Recursive CTE来解决这个查询(我确实从上面的@Max解决方案中得到了帮助)-

declare @Today date = getdate()
declare @CalendarMonth int = datepart(month, @Today)
declare @LastFinancialQuarterDate date = dateadd(month, (case when @CalendarMonth <= 6 then 6 else 18 end) - @CalendarMonth, @Today)

;with cte as
(
    select  cast(datepart(year, @LastFinancialQuarterDate) as varchar(4))
            +
            cast(datepart(quarter, @LastFinancialQuarterDate) as varchar(1)) as ID,
            1 as QuartersAgo
    union all
    select  cast(datepart(year, dateadd(month, cte.QuartersAgo * -3, @LastFinancialQuarterDate)) as varchar(4))
            +
            cast(datepart(quarter, dateadd(month, cte.QuartersAgo * -3, @LastFinancialQuarterDate)) as varchar(1)) as ID,
            (cte.QuartersAgo + 1) as QuartersAgo
    from cte
    where cte.QuartersAgo < 4
)
select * from cte

我做的第一件事是找到属于上一个澳大利亚季度的日期并将其设置为 @LastFinancialQuarterDate 变量。之后,我在 Recursive CTE 中使用了该变量来遍历最后四个季度。