Reputation: 135
I have a big data set (100+ mil. rows) that i need to split to rows based on the number of months between the start_date and end_date for accounting purposes.
So for example this record should split into 3612 rows because theres 3612 months between start_date and end_date:
The result I am after should look like this:
I know I can achieve this using recursive CTE, but it takes FOREVER for dataset this big. Any more effective ways to do this?
Upvotes: 0
Views: 426
Reputation: 20362
I just had to do the exact same thing less than 1 week ago!
-----DLL
CREATE TABLE Reporting_Table (
Credit_Line_NO Varchar(10),
noMonths INT,
EFFECTIVEDATE Date,
EXPIRY_DATE Date,
Amount Money,
mxDays INT,
mxFactor decimal(5,4),
Calc Money)
INSERT INTO Reporting_Table (Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc)
Values('9938810','3','3/31/2018','6/12/2020','11718.75','90','1','11718.75')
INSERT INTO Reporting_Table (Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc)
Values('2235461','1','6/30/2018','6/6/2019','12345','30','1','12345')
INSERT INTO Reporting_Table (Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc)
Values('3365434','12','6/30/2018','6/30/2019','298523.36085','365','1.01388888888889','302669.518639583')
---- VIEW DATA SET . . .
Select *, (DATEDIFF(MONTH,EFFECTIVEDATE,EXPIRY_DATE)/noMonths)+1 as FREQ
From Reporting_Table
-- Now that you have a working data set to fiddle around with, simply expand all relevant tows.
;with cte as
(
select Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc,
(DATEDIFF(MONTH,EFFECTIVEDATE,EXPIRY_DATE)/noMonths)+1 as FREQ,
1 as Rec_Iteration
from Reporting_Table
where noMonths > 1
union all
select Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc,
(DATEDIFF(MONTH,EFFECTIVEDATE,EXPIRY_DATE)/noMonths)+1 as FREQ,
Rec_Iteration + 1
from cte
where Rec_Iteration < FREQ
)
select Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc,
(DATEDIFF(MONTH,EFFECTIVEDATE,EXPIRY_DATE)/noMonths)+1 as FREQ
from cte
order by Credit_Line_NO,
Rec_Iteration
Upvotes: 0
Reputation: 45106
declare @numbers TABLE (ID int NOT NULL primary key);
INSERT INTO @numbers (ID)
VALUES
(1)
,(2)
,(3)
,(4)
,(5)
,(6)
,(7)
,(8);
declare @range TABLE (cnt int);
INSERT INTO @range (cnt)
VALUES (1), (4), (6);
select r.cnt, n.ID
from @range r
join @numbers n
on n.ID < = r.cnt
order by r.cnt, n.ID;
Upvotes: 0
Reputation: 4146
In this query I'm using master.dbo.spt_values
as tally table. But it will not suit you since it has numbers only till 2048. So you must create your own number table and change master.dbo.spt_values
with it in the query.
declare @t table (
id int
, start_date date
, end_date date
, months int
)
insert into @t
values (1, '19990101', '21000101', 1212)
select
t.id, month_num = v.number + 1
, total_days = sum(datediff(dd, dateadd(mm, v.number, t.start_date), dateadd(mm, v.number + 1, t.start_date))) over (partition by t.id)
, t.start_date, t.end_date
from
@t t
join master.dbo.spt_values v on t.months > v.number
where
v.type = 'P'
Upvotes: 3