Reputation: 2922
I'm learning SQL and I have this horrible looking query to select the last 4 financial/fiscal quarters and how many quarters ago they were.
If the date is 28th March 2018 then the table returned is like:
╔═══╦════════════╦═════════════╗
║ ║ ID ║ QuartersAgo ║
╠═══╬════════════╬═════════════╣
║ 1 ║ 20182 ║ 1 ║
║ 2 ║ 20181 ║ 2 ║
║ 3 ║ 20174 ║ 3 ║
║ 4 ║ 20173 ║ 4 ║
╚═══╩════════════╩═════════════╝
My query is below. Surely there is a much better and more efficient way... What I like about this is that it works and I can commit it to version control (unlike Excel and PowerBI that I would usually build this thing in).
In the first phase I am determining the financial year based on the month of the calendar year.
In a similar way I am then breaking the years down into Quarters to determine the current financial quarter.
Once I have found that I am setting and determining values for the nth quarter ago.
What I don't like is that I feel like I should just be inserting values into the variable table as I am determining which quarter I am. Instead I assign to a value and then insert that later on.
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
Would appreciate some feedback :) Thanks
Upvotes: 2
Views: 3489
Reputation: 2032
I have also use Recursive CTE
to solve this query (I did take help from above @Max solution)-
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
First thing I did is to find date which belongs to last Australian quarter and set it to @LastFinancialQuarterDate
variable. After that I have use that variable in Recursive CTE
to traverse last four quarters.
Upvotes: 0
Reputation: 4334
Recursive CTE to the rescue:
;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
Here's the output:
FinancialQuarter QuartersAgo
20182 1
20181 2
20174 3
20173 4
Here are some important points:
To get today's date as an Australian fiscal quarter, add 3 months and concat the year to the quarter (which you can get with DATEPART(QUARTER(DATE)), so, like this:
CAST(YEAR(DATEADD(MONTH, 3, GETDATE())) AS VARCHAR(4)) + CAST(DATEPART(QUARTER, DATEADD(MONTH, 3, GETDATE())) AS VARCHAR(1))
Upvotes: 3