I am trying to update an ERP software and I block on a syntax in a table made with PIVOTS
Let me explain, I would like in my table (which is functional) that all NULL values be replaced by a 0.
In another table almost identical with the same syntax I succeeded but in the other, the COALESCEs do not work .. I'm stuck on it for quite some time. If someone can enlighten me ..
Here is the beginning of the code for the table where the COALESCE functions (I put only the beginning because the code makes 500 lines and the remainder remains the same)
DECLARE #VALEUR VARCHAR(10)
SET #VALEUR = RTRIM(CAST(:A_USER AS VARCHAR(10)))
--Cette valeur va correspondre à l'id de la personne connectée
SET language us_english
DECLARE #P_A_USER VARCHAR(10)
SELECT #P_A_USER = VALEUR FROM T_PARAMETRE
WHERE PARAGRAPHE = 'COD_PRTDIRCOMM'
DECLARE #COD_GRP VARCHAR(10)
SELECT #COD_GRP = COD_GRP FROM USERS WHERE COD_USER = #VALEUR
IF #P_A_USER = 'GBL'
BEGIN
SET #P_A_USER = NULL
END
SELECT *
FROM (SELECT 'CA Total facturé' AS DONNEE
UNION
SELECT 'Marge total (en €)' AS DONNEE
UNION
SELECT 'Marge total (en %)' AS DONNEE
UNION
SELECT 'CA Exploitation facturé' AS DONNEE
UNION
SELECT 'Marge Exploitation (en €)' AS DONNEE
UNION
SELECT 'Marge Exploitation (en %)' AS DONNEE
UNION
SELECT 'CA Service facturé' AS DONNEE
UNION
SELECT 'Marge Service (en €)' AS DONNEE
UNION
SELECT 'Marge Service (en %)' AS DONNEE
UNION
SELECT 'CA Solution facturé' AS DONNEE
UNION
SELECT 'Marge Solution facturé (en €)' AS DONNEE
UNION
SELECT 'Marge Solution facturé (en %)' AS DONNEE) Q
LEFT OUTER JOIN (
-----------------------------------DEBUT TOTAL----------------------------
SELECT donnee,
NUMLIGNE,
COALESCE(january, 0) AS JANUARY,
COALESCE(february, 0) AS FEBRUARY,
COALESCE(march, 0) AS MARCH,
COALESCE(april, 0) AS APRIL,
COALESCE(may, 0) AS MAY,
COALESCE(june, 0) AS JUNE,
COALESCE(july, 0) AS JULY,
COALESCE(august, 0) AS AUGUST,
COALESCE(september, 0) AS SEPTEMBER,
COALESCE(october, 0) AS OCTOBER,
COALESCE(november, 0) AS NOVEMBER,
COALESCE(december, 0) AS DECEMBER
FROM (SELECT Datename(month, date_facture) AS 'MOIS'
,
Cast(Sum(n1)AS DECIMAL (18, 2)) AS
'CA_Total_mois',
'CA total facturé' AS 'DONNEE',
'A' AS 'NUMLIGNE'
FROM v_facture
WHERE cod_com = ISNULL(#P_A_USER, cod_com)
AND Year(date_facture) = '2016'
GROUP BY Datename(month, date_facture)) AS ca_total_mois
PIVOT(Sum(ca_total_mois)
FOR mois IN (january,
february,
march,
april,
may,
june,
july,
august,
september,
october,
november,
december)) AS pvt1
-------------------------------------------------------------------------
UNION ALL
SELECT donnee,
NUMLIGNE,
COALESCE(january, 0) AS JANUARY,
COALESCE(february, 0) AS FEBRUARY,
COALESCE(march, 0) AS MARCH,
COALESCE(april, 0) AS APRIL,
COALESCE(may, 0) AS MAY,
COALESCE(june, 0) AS JUNE,
COALESCE(july, 0) AS JULY,
COALESCE(august, 0) AS AUGUST,
COALESCE(september, 0) AS SEPTEMBER,
COALESCE(october, 0) AS OCTOBER,
COALESCE(november, 0) AS NOVEMBER,
COALESCE(december, 0) AS DECEMBER
FROM (SELECT Datename(month, date_facture) AS 'MOIS',
Cast(Sum(n2)AS DECIMAL (18, 2)) AS
'Marge_Total_mois',
'Marge total (en €)' AS 'DONNEE',
'B' AS 'NUMLIGNE'
FROM v_facture
WHERE cod_com = ISNULL(#P_A_USER, cod_com)
AND Year(date_facture) = '2016'
GROUP BY Datename(month, date_facture)) AS
Marge_Total_Mois
PIVOT(Sum(marge_total_mois)
FOR mois IN (january,
february,
march,
april,
may,
june,
july,
august,
september,
october,
november,
december)) AS pvt2
------------------------------------------------------------------------
UNION ALL
-- Etc ...
-- The end :
) Q2
ON Q2.donnee = Q.donnee
ORDER BY NUMLIGNE ASC
Here is the table he refers to:
1st TABLE
And here is the code where the COALESCEs do not work:
DECLARE #VALEUR VARCHAR(10)
SET #VALEUR = RTRIM(CAST(:A_USER AS VARCHAR(10)))
-- This value will match the id of the connected person
SET language us_english
DECLARE #P_A_USER VARCHAR(10)
SELECT #P_A_USER = VALEUR FROM T_PARAMETRE
WHERE PARAGRAPHE = 'COD_PRTDIRCOMM'
DECLARE #COD_GRP VARCHAR(10)
SELECT #COD_GRP = COD_GRP FROM USERS WHERE COD_USER = #VALEUR
IF #P_A_USER = 'GBL'
BEGIN
SET #P_A_USER = NULL
END
SELECT *
FROM (SELECT 'Nouveaux contrats' AS DONNEE
UNION
SELECT 'Contrats Renouvelés' AS DONNEE
UNION
SELECT 'RDV Réalisés' AS DONNEE
UNION
SELECT 'RDV Planifiés' AS DONNEE
UNION
SELECT 'Nouveaux comptes ouverts' AS DONNEE
UNION
SELECT 'Nouvelles affaires' AS DONNEE
UNION
SELECT 'Nvl affaires avec exploitations' AS DONNEE
UNION
SELECT 'CA Nouvelles affaires (€)' AS DONNEE
UNION
SELECT 'Affaires gagnées' AS DONNEE
UNION
SELECT 'Affaires perdues' AS DONNEE
UNION
SELECT 'Taux affaires gagnées' AS DONNEE) Q
LEFT OUTER JOIN (
-----------------------------------DEBUT TOTAL----------------------------
SELECT donnee,
NUMLIGNE,
COALESCE(january, 0) AS JANUARY,
COALESCE(february, 0) AS FEBRUARY,
COALESCE(march, 0) AS MARCH,
COALESCE(april, 0) AS APRIL,
COALESCE(may, 0) AS MAY,
COALESCE(june, 0) AS JUNE,
COALESCE(july, 0) AS JULY,
COALESCE(august, 0) AS AUGUST,
COALESCE(september, 0) AS SEPTEMBER,
COALESCE(october, 0) AS OCTOBER,
COALESCE(november, 0) AS NOVEMBER,
COALESCE(december, 0) AS DECEMBER
FROM (SELECT Datename(month, d1) AS 'MOIS'
,
Cast(Count(*)AS DECIMAL (18, 2)) AS
'Total_Nouveaux_Contrats',
'Nouveaux contrats' AS 'DONNEE',
'A' AS 'NUMLIGNE'
FROM contrat
WHERE c22 = ISNULL(#P_A_USER, c22)
AND Month(D1)= MONTH(getdate())
AND C27 = 'Nouveau contrat'
GROUP BY Datename(month, d1)) AS Total_Nouveaux_Contrats
PIVOT(Sum(Total_Nouveaux_Contrats)
FOR mois IN (january,
february,
march,
april,
may,
june,
july,
august,
september,
october,
november,
december)) AS pvt1
-------------------------------------------------------------------------
UNION ALL
SELECT donnee,
NUMLIGNE,
COALESCE(january, 0) AS JANUARY,
COALESCE(february, 0) AS FEBRUARY,
COALESCE(march, 0) AS MARCH,
COALESCE(april, 0) AS APRIL,
COALESCE(may, 0) AS MAY,
COALESCE(june, 0) AS JUNE,
COALESCE(july, 0) AS JULY,
COALESCE(august, 0) AS AUGUST,
COALESCE(september, 0) AS SEPTEMBER,
COALESCE(october, 0) AS OCTOBER,
COALESCE(november, 0) AS NOVEMBER,
COALESCE(december, 0) AS DECEMBER
FROM (SELECT Datename(month, d1) AS 'MOIS',
Cast(Count(*)AS DECIMAL (18, 2)) AS
'Total_Contrats_Renouveles',
'Contrats Renouvelés' AS 'DONNEE',
'B' AS 'NUMLIGNE'
FROM contrat
WHERE c22 = ISNULL(#P_A_USER, c22)
AND Month(D1)= MONTH(getdate())
AND ( c27 = NULL
OR c27 = 'nouveau contrat' )
GROUP BY Datename(month, d1)) AS
Total_Contrats_Renouveles
PIVOT(Sum(Total_Contrats_Renouveles)
FOR mois IN (january,
february,
march,
april,
may,
june,
july,
august,
september,
october,
november,
december)) AS pvt2
------------------------------------------------------------------------
UNION ALL
-- ETC ...
-- La fin :
) Q2
ON Q2.donnee = Q.donnee
ORDER BY NUMLIGNE ASC
And here is the table it returns such a user for both for which there is not much value
2nd TABLE
I noticed that as long as there is a value in a row that is different from 0, then it will display all the other values of that row that are null at 0 (the coalesces would work in this case there o_O)
So, sorry for the novel and thank you for your attention!
I am using SSMS for test my request but I use the code in the ERP software
I haven't much to go on here but it looks like a missing 's'. I think the COALESCE is working but the LEFT OUTER JOIN is not.
SELECT *
FROM (SELECT 'Nouveaux contrats' AS DONNEE
UNION
and later
WHERE c22 = ISNULL(#P_A_USER, c22)
AND Month(D1)= MONTH(getdate())
AND ( c27 = NULL
OR c27 = 'nouveau contrat' )
GROUP BY Datename(month, d1)) AS
Try including the name from both sides of the join to ensure the row is coming back at all.
Related
I have a table that looks like this:
MONTH
NAME
July
Ally
July
Don
July
Ken
March
Lee
March
Froyo
March
Denise
April
Kram
I want it to look like this:
July
March
April
Ally
Lee
Kram
Don
Froyo
Ken
Denise
Here is a solution I have tried:
SELECT
(SELECT name FROM mytable WHERE month='July') AS July,
(SELECT name FROM mytable WHERE month='March') AS March,
(SELECT name FROM mytable WHERE month='April') AS April
FROM DUAL;
However, I do not want to hardcode the month values like month='July'
Is there any way to do this?
Seems you're trying to construct a query with conditional aggregate, sorting the month-columns in their order within a year such as
SELECT MAX(CASE
WHEN month = 'March' THEN
name
END) AS March,
MAX(CASE
WHEN month = 'April' THEN
name
END) AS April,
MAX(CASE
WHEN month = 'July' THEN
name
END) AS July
FROM (SELECT t.*,
ROW_NUMBER() OVER(PARTITION BY month ORDER BY name) AS rn
FROM t) -- "t" represents your mentioned table
GROUP BY rn
ORDER BY rn
and you can generate a stored function code as you wish to make it dynamic such as
CREATE OR REPLACE FUNCTION Get_People_By_Months RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols VARCHAR2(32767);
BEGIN
SELECT LISTAGG('MAX(CASE WHEN month = '''||month||''' THEN name END) AS '||month ,',')
WITHIN GROUP (ORDER BY month_nr)
INTO v_cols
FROM (SELECT DISTINCT t.month, m.month_nr
FROM t
JOIN (SELECT TO_CHAR(TO_DATE(level, 'mm'),
'Month',
'NLS_DATE_LANGUAGE=English') AS month,
level AS month_nr
FROM dual
CONNECT BY level <= 12) m
ON t.month = TRIM(m.month));
v_sql :='SELECT '|| v_cols ||'
FROM (SELECT t.*, ROW_NUMBER() OVER (PARTITION BY month ORDER BY name) AS rn
FROM t)
GROUP BY rn
ORDER BY rn';
OPEN v_recordset FOR v_sql;
DBMS_OUTPUT.PUT_LINE(v_sql);
RETURN v_recordset;
END;
/
then invoke from the SQL Developer's console as
SQL> DECLARE
result SYS_REFCURSOR;
BEGIN
:result := Get_People_By_Months;
END;
/
SQL> PRINT result;
I have following 2019 data.
Date Calendar_year Weekend_indicator
2019-01-01 2019 weekday
2019-01-01 2019 weekday
2019-01-02 2019 weekday
2019-01-02 2019 weekday
and so on.
I need to give one record a day and another a night value and have it repeated for the entire year's data so that it would look like this.
Date Calendar_year Weekend_indicator day_night
2019-01-01 2019 weekday Day
2019-01-01 2019 weekday Night
2019-01-02 2019 weekday Day
2019-01-02 2019 weekday Night
Here is my code.
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2019
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data;
WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT c.FromDate AS Date
,#Year as calendar_year
,CHOOSE(datepart(dw, c.FromDate), 'WEEKEND', 'WEEKDAY', 'WEEKDAY',
'WEEKDAY', 'WEEKDAY', 'WEEKDAY', 'WEEKEND') as weekend_indicator
FROM CTE c
CROSS JOIN ( values (1), (2) ) tb (FromDate)
WHERE DayName IN ('Saturday','Sunday')
or dayname not in ('Saturday', 'Sunday')
order by c.FromDate
OPTION (MaxRecursion 1000)
How do I assign the day and the night value and have it repeated?
This simple query might point you in the right direction:
USE TEMPDB
CREATE TABLE #T (DateCol DATE, Calender_Year INT)
INSERT INTO #T VALUES ('20180101', 2018 )
SELECT *
FROM #T
CROSS APPLY (VALUES ('Day'), ('Night') ) AS C (Val)
If you want just to change your code without rewriting it,
add one more row as below:
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2019
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0);
-- Creating Query to Prepare Year Data;
WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT c.FromDate AS Date
,#Year as calendar_year
,CHOOSE(datepart(dw, c.FromDate), 'WEEKEND', 'WEEKDAY', 'WEEKDAY',
'WEEKDAY', 'WEEKDAY', 'WEEKDAY', 'WEEKEND') as weekend_indicator,
case tb.FromDate when 1 then 'Day' else 'Night' end as day_night -----<<<<<<-----
FROM CTE c
CROSS JOIN ( values (1), (2) ) tb (FromDate)
WHERE DayName IN ('Saturday','Sunday')
or dayname not in ('Saturday', 'Sunday')
order by c.FromDate
OPTION (MaxRecursion 1000)
I use a table variable for create this solution.
DECLARE
#Year int
,#FirstDateOfYear date
,#LastDateOfYear date
,#date_loop date
SET #YEAR = 2019
SELECT
#FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
,#LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
,#date_loop = DATEADD(yyyy, #Year - 1900, 0) --initialize variable for loop
DECLARE #date_table TABLE ([Date] date, [Calendar_year] int, [Weekend_indicator] varchar(10), [day_night] varchar(5))
WHILE #date_loop < #LastDateOfYear
BEGIN
INSERT #date_table
SELECT d.[Date], d.[Calendar_year], d.[Weekend_indicator], ca.[day_night]
FROM (
SELECT
#date_loop AS [Date]
,YEAR(#date_loop) AS [Calendar_year]
,CHOOSE(datepart(dw, #date_loop), 'weekend', 'weekday', 'weekday','weekday', 'weekday', 'weekday', 'weekend') AS [Weekend_indicator]) AS d
CROSS APPLY (
SELECT 'Day' AS [day_night]
UNION
SELECT 'Night'
) AS ca
SET #date_loop = DATEADD(day,1,#date_loop)
END
SELECT *
FROM #date_table
If you don't have a calendar or numbers table, you can use an ad-hoc table
Example
Declare #Date1 date = '2019-01-01'
Declare #Date2 date = '2019-12-31'
Select [Date] = d
,Calendar_year = datepart(YEAR,d)
,Weekend_indicator = case when datename(WEEKDAY,d) in ('Saturday','Sunday') then 'weekend' else 'weekday' end
,day_night
From (
Select Top (DateDiff(DAY,#Date1,#Date2)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),#Date1)
From master..spt_values n1,master..spt_values n2
) A
Cross Join (values ('Day'),('Night') ) b(day_night)
Order by D,day_night
Returns
Date Calendar_year Weekend_indicator day_night
2019-01-01 2019 weekday Day
2019-01-01 2019 weekday Night
2019-01-02 2019 weekday Day
2019-01-02 2019 weekday Night
...
2019-12-30 2019 weekday Day
2019-12-30 2019 weekday Night
2019-12-31 2019 weekday Day
2019-12-31 2019 weekday Night
Revised answer: I see an unused (VALUES ...) clause which could be used like so:
CROSS JOIN (VALUES ('Day'), ('Night') ) whatever(day_night)
DB Fiddle
I have a table Product with the following columns
ProductId Name RegistrationDate UnregistrationDate
1 AB 2013-01-01 2013-03-01
2 CD 2013-01-10 2013-03-13
etc
I would like to get a list of Registered Products per every month of a year.
Example : Year , Month and the number of dealers which are registered and not unregistered.
Year Month RegisteredProucts
2013 2 35
2013 3 45(includes products even registered before March 2013)
I wrote the follwing stored procedure to find the Registered products for one month:
& it works :
#Begin Time = First Day of the Month
#End Time = Last Day of the Month
select COUNT(DISTINCT P.ProductId) as RegisteredProducts from Product P
where ((P.RegisteredDate < #EndTime)
AND (P.UnregisteredDate > #EndTime))
I then wrote the query below but it seems to group the results by the RegisteredDate.
I would like to know how I can group registered products (which are not unregistered) by the end of each month
for a duration of one year ?
select YEAR(P.RegisteredDate) AS [YEAR],MONTH(P.RegisteredDate) AS [MONTH], COUNT(DISTINCT P.ProductId) as RegisteredProducts from Product P
where ((P.RegisteredDate < #EndTime)
AND (P.UnregisteredDate > #EndTime))
group by YEAR(D.RegisteredDate), MONTH(D.RegisteredDate)
WITH months (mon) AS
(
SELECT CAST('2013-01-01' AS DATE) AS mon
UNION ALL
SELECT DATEADD(month, 1, mon)
FROM months
WHERE mon < DATEADD(month, -1, GETDATE())
)
SELECT mon, COUNT(productId)
FROM months
LEFT JOIN
registeredProducts
ON registrationDate < DATEADD(month, 1, mon)
AND (unregistrationDate >= mon OR unregistrationDate IS NULL)
GROUP BY
mon
; with months as
(
select cast('2013-01-01' as date) as dt
union all
select dateadd(month, 1, dt)
from months
where dt < '2014-01-01'
)
select *
from months m
cross apply
(
select count(*) as ProductCount
from Product p
where p.RegistrationDate < dateadd(month, 1, m.dt) and
(
UnregistrationDate is null
or UnregistrationDate >= m.dt
)
) p
Example at SQL Fiddle.
I have incoming hourly data (spanning 5 years) with timestamps recorded in CST or EST. I want to store this in my database in UTC time.
Is there a way for me to convert from CST/CDT/CPT or EST/EDT/EPT to UTC using TSQL?
mI would recommend you store these as DATETIMEOFFSET to preserve the timezone information.
If you need to display then as UTC Dates then you can use SWITCHOFFSET
You can determine is datetime in row in EST or EDT:
Since 2007, the local time changes at
02:00 EST to 03:00 EDT on the second
Sunday in March and returns at 02:00
EDT to 01:00 EST on the first Sunday
in November, in the U.S. as well as in
Canada.
Then apply DATEADD()
Mb my source code better explains what I mean:
declare #t table(dt datetime)
insert #t values ('2011-07-06T10:00:00'), ('2011-01-01T00:00:00'), ('2011-03-12T00:00:00'),
('2006-07-06T10:00:00')
select b.dt
, CASE
WHEN b.dt between b.[edt_start] and [edt_end]
THEN DATEADD(HH, -5, b.dt)
ELSE DATEADD(HH, -4, b.dt)
END
, CASE
WHEN b.dt between b.[edt_start] and [edt_end]
THEN '-05:00'
ELSE '-04:00'
END
from
(
select a.dt
, DATEADD(HH, 2, CASE
WHEN DATEPART(WEEKDAY, a.march) = 1
THEN a.march
ELSE DATEADD(DAY, 15 - DATEPART(WEEKDAY, a.march), a.march)
END) [edt_start]
, DATEADD(HH, 2, CASE
WHEN DATEPART(WEEKDAY, a.november) = 1
THEN a.march
ELSE DATEADD(DAY, 8 - DATEPART(WEEKDAY, a.november), a.november)
END) [edt_end]
from
(
select t.dt
, YEAR(t.dt) [year]
, CAST(CAST(YEAR(t.dt) as varchar(4)) + '03' + '01' as datetime) [march]
, CAST(CAST(YEAR(t.dt) as varchar(4)) + '11' + '01' as datetime) [november]
from #t t
)a
)b
I need to split these dates by FY and get the number of months between the dates. I have the FY split answered previously at How to duplicate Rows with new entries link.
Having the following dates:-
ID Start dt End dt
2550 10/1/2010 9/30/2011
2551 8/1/2014 7/31/2015
2552 6/1/2013 5/31/2015
2553 5/10/2012 6/11/2014
I would like the following result set:
ID FY # of Months Start Dt End Dt
2550 2011 12 10/1/2010 9/30/2011
2551 2014 2 8/1/2014 9/30/2014
2551 2015 10 10/1/2014 7/31/2015
2552 2013 4 6/1/2013 9/30/2013
2552 2014 12 10/1/2013 9/30/2014
2552 2015 8 10/1/2014 5/31/2015
2553 2012 5 5/10/2012 9/30/2012
2553 2013 12 10/1/2012 9/30/2013
2553 2014 9 10/1/2013 6/11/2014
An FY is considered from Oct to Sept of the next year.
Thanks in advance!
It was a fun query to build:
declare #t table(id int, start_dt datetime, end_dt datetime)
Insert into #t(id, start_dt, end_dt) Values
(2550, '2010/10/1', '2011/9/30')
, (2551, '2014/8/1', '2015/7/31')
, (2552, '2013/6/1', '2015/5/31')
, (2553, '2012/5/10', '2014/6/11')
, (2554, '2012/5/10', '2012/11/1')
, (2555, '2012/5/10', '2012/8/11')
; with data as(
Select id, start_dt, end_dt, next_y = DATEADD(month, 9, DATEADD(year, DATEDIFF(year, 0, DATEADD(month, 3, start_dt)), 0))
From #t
), split as (
Select id, start_dt
, end_dt = case when next_y > end_dt then end_dt else DATEADD(day, -1, next_y) end
, next_y = DATEADD(year, 1, next_y)
From data as d
Union All
Select s.id
, DATEADD(day, 1, s.end_dt)
, case when DATEADD(day, -1, next_y) < t.end_dt then DATEADD(day, -1, next_y) else t.end_dt end
, next_y = DATEADD(year, 1, s.next_y)
From split as s
Inner Join #t as t on t.id = s.id
Where s.end_dt < t.end_dt
)
Select ID, FY = year(end_dt), '# of Months' = DATEDIFF(month, start_dt, end_dt)+1, 'Start Dt' = start_dt, 'End Dt' = end_dt
From split
Order by id, start_dt, end_dt