Recursive CTE for Waterfall calculation - tsql

I have a Table #BD as shown below
Table: #BD
Fac_id PayerType Current over_30 over_60 over_90 over_120
136 HOSPICE 0.1977 0.3095 0.3649 0.4688 0.1197
136 INSURANCE 0.0947 0.3502 0.7798 -0.5086 -0.245
136 MCAID 0.6115 0.7351 0.8535 0.6048 -1.8026
I am trying to get the output shown below - based on this calculation
The 'Current' column value (197.70) for HOSPICE is calculated as 1000 * 0.1977 (from table #BD)
The over_30 column value for HOSPICE 61.19 = 197.70 * 0.3095
The over_60 column value for HOSPICE 22.33 = 61.19 * 0.3649
1000
Fac_id PayerType Current over_30 over_60 over_90 over_120
136 HOSPICE 197.70 61.19 22.33 10.47 1.25
136 INSURANCE 94.70 33.16 25.86 (13.15) 3.22
136 MCAID 611.50 449.51 383.66 232.04 (418.27)
Is it possible to do this using recursive CTE?
Or what is the best approach to this waterfall calculation?

You can use multiple sub query; or take it from here to any better better approach:
CREATE TABLE #BD
(Fac_id int, PayerType varchar(20), [Current] float, over_30 float, over_60 float, over_90 float, over_120 float)
insert into #BD
values
(136, 'HOSPICE', 0.1977, 0.3095, 0.3649, 0.4688, 0.1197),
(136, 'INSURANCE', 0.0947, 0.3502, 0.7798, -0.5086, -0.245),
(136, 'MCAID', 0.6115, 0.7351, 0.8535, 0.6048, -1.8026)
select * from #BD
SELECT fac_id, payerType, e.[current], e.over_30, e.over_60, e.over_90, (e.over_90 * over_120) AS over_120
FROM (select fac_id, payerType, c.[current], c.over_30, c.over_60, (c.over_60 * over_90) AS over_90, over_120
FROM (select fac_id, payerType, b.[current], b.over_30, (b.over_30 * over_60) AS over_60, b.over_90, b.over_120
FROM (SELECT fac_id, a.PayerType, a.[current], a.[current] * over_30 AS over_30, over_60, over_90, over_120
FROM (SELECT fac_id, #BD.payertype, over_30, ([Current] *1000) AS [current], over_60, over_90, over_120 FROM #BD) a) b) c) e

Related

Unable to calculate compound interest in PostgreSQL

I have a table table1 which contains the details of any depositor like
Depositor
Deposit_Amount
Deposit_Date
Maturity_Date
Tenure
Rate
A
25000
2021-08-10
2022-08-10
12
10%
I have another table table2 which contains the interest due date as:
Interest_Due_Date
2021-09-30
2021-12-31
2022-03-31
2022-06-30
2022-08-10
My Code is:
with recursive recur (n, start_bal, days,principle,interest, end_bal) as
(
select sno,deposit_amount,rate,days,deposit_amount * (((rate::decimal(18,2))/100)/365)*days as interest, deposit_amount+(deposit_amount * (((rate::decimal(18,2))/100)/365)*days) as end_bal from (
SELECT
sno, COALESCE(DATE_PART('day', deposit_date::TIMESTAMP - lag(deposit_date::TIMESTAMP) over
(ORDER BY sno ASC rows BETWEEN UNBOUNDED PRECEDING AND CURRENT row)),0) AS
days, deposit_date, deposit_amount, rate
FROM
( SELECT
ROW_NUMBER () OVER (ORDER BY deposit_date) AS sno,
deposit_date,
deposit_amount,
rate
FROM
( SELECT
t1.deposit_date, t1.deposit_amount, t1.rate from table1 t1
UNION ALL
SELECT
t2.Interest_Due_Date AS idate, 0 as depo_amount, 0 as rate
FROM
table2 t2
ORDER BY
deposit_date) dep) calc) b where sno = 1 union all select b.sno, b.end_bal,b.days,b.prin_bal,(coalesce(a.end_bal,0)) * (((b.rate)/100)/365)*b.days as interest_NEW,
coalesce(a.end_bal,0)+ ((a.end_bal) * (((calc.rate)/100)/365)*calc.days) as end_bal_NEW
from b, recur as a
where calc.sno = a.n+1 ) select * from recur
"Every time when i try to execute the query its showing an error 'relation 'b' does not exist"
...
The result table should be
Deposit Amount
Date
Days
Interest
Total Amount
25000
2021-08-10
0
0
25000
0
2021-09-30
51
349.32
25349.32
0
2021-12-31
92
638.94
25988.26
0
2022-03-31
90
640.81
26629.06
0
2022-06-30
91
663.90
27292.97
0
2022-08-10
41
306.58
27599.54

bulk update one table using value in another table

If I find a max value in my database of LIM50177
lim_id
LIM50172
LIM50173
LIM50174
LIM50175
LIM50176
LIM50177
How can I loop through another table and for every base_id go and bulk replace the temp_id with a new lim_id?
temp_id base id desc
1008 720 GP
1009 721 GT
1010 722 GA
1021 723 P
1021 724 G
1021 725 X
In other words
The data will be updated as follows:
temp_id base id desc
LIM50178 720 GP
LIM50179 721 GT
LIM50180 722 GA
LIM50181 723 P
LIM50182 724 G
LIM50183 725 X
Use a sequence every time you generate the lim_id values so you get unique values.
(Don't use the values in the other table to calculate the maximum value as, if the table is updated in two sessions at the same time and you are always basing the next value off the maximum value in the table then neither session will see the updates performed by the other session and you can end up generating identical "next" values in each session. Instead, every time you generate the "next" value always get that "next" value from the sequence.)
Oracle Setup:
CREATE SEQUENCE lim_id_seq START WITH 50178;
CREATE TABLE temp_data ( temp_id, base_id, "desc" ) AS
SELECT CAST( 1008 AS VARCHAR2(10) ), 720, 'GP' FROM DUAL UNION ALL
SELECT CAST( 1009 AS VARCHAR2(10) ), 721, 'GT' FROM DUAL UNION ALL
SELECT CAST( 1010 AS VARCHAR2(10) ), 722, 'GA' FROM DUAL UNION ALL
SELECT CAST( 1021 AS VARCHAR2(10) ), 723, 'P' FROM DUAL UNION ALL
SELECT CAST( 1021 AS VARCHAR2(10) ), 724, 'G' FROM DUAL UNION ALL
SELECT CAST( 1021 AS VARCHAR2(10) ), 725, 'X' FROM DUAL
Update using the Sequence:
UPDATE temp_data
SET temp_id = 'LIM' || lim_id_seq.NEXTVAL;
Result:
SELECT * FROM temp_data;
TEMP_ID | BASE_ID | desc
:------- | ------: | :---
LIM50178 | 720 | GP
LIM50179 | 721 | GT
LIM50180 | 722 | GA
LIM50181 | 723 | P
LIM50182 | 724 | G
LIM50183 | 725 | X
db<>fiddle here

Get Data Week Wise in SQL Server

I have a Table with columns ProductId, DateofPurchase, Quantity.
I want a report in which week it belongs to.
Suppose if I give March Month I can get the quantity for the march month.
But I want as below if I give date as parameter.
Here Quantity available for March month on 23/03/2018 is 100
Material Code Week1 Week2 Week3 Week4
12475 - - - 100
The logic is 1-7 first week, 8-15 second week, 16-23 third week, 24-30 fourth week
#Sasi, this can get you started. YOu will need to use CTE to build a template table that describes what happens yearly. Then using your table with inner join you can link it up and do a pivot to group the weeks.
Let me know if you need any tweaking.
DECLARE #StartDate DATE='20180101'
DECLARE #EndDate DATE='20180901'
DECLARE #Dates TABLE(
Workdate DATE Primary Key
)
DECLARE #tbl TABLE(ProductId INT, DateofPurchase DATE, Quantity INT);
INSERT INTO #tbl
SELECT 12475, '20180623', 100
;WITH Dates AS(
SELECT Workdate=#StartDate,WorkMonth=DATENAME(MONTH,#StartDate),WorkYear=YEAR(#StartDate), WorkWeek=datename(wk, #StartDate )
UNION ALL
SELECT CurrDate=DateAdd(WEEK,1,Workdate),WorkMonth=DATENAME(MONTH,DateAdd(WEEK,1,Workdate)),YEAR(DateAdd(WEEK,1,Workdate)),datename(wk, DateAdd(WEEK,1,Workdate)) FROM Dates D WHERE Workdate<#EndDate ---AND (DATENAME(MONTH,D.Workdate))=(DATENAME(MONTH,D.Workdate))
)
SELECT *
FROM
(
SELECT
sal.ProductId,
GroupWeek='Week'+
CASE
WHEN WorkWeek BETWEEN 1 AND 7 THEN '1'
WHEN WorkWeek BETWEEN 8 AND 15 THEN '2'
WHEN WorkWeek BETWEEN 16 AND 23 THEN '3'
WHEN WorkWeek BETWEEN 24 AND 30 THEN '4'
WHEN WorkWeek BETWEEN 31 AND 37 THEN '5'
WHEN WorkWeek BETWEEN 38 AND 42 THEN '6'
END,
Quantity
FROM
Dates D
JOIN #tbl sal on
sal.DateofPurchase between D.Workdate and DateAdd(DAY,6,Workdate)
)T
PIVOT
(
SUM(Quantity) FOR GroupWeek IN (Week1, Week2, Week3, Week4, Week5, Week6, Week7, Week8, Week9, Week10, Week11, Week12, Week13, Week14, Week15, Week16, Week17, Week18, Week19, Week20, Week21, Week22, Week23, Week24, Week25, Week26, Week27, Week28, Week29, Week30, Week31, Week32, Week33, Week34, Week35, Week36, Week37, Week38, Week39, Week40, Week41, Week42, Week43, Week44, Week45, Week46, Week47, Week48, Week49, Week50, Week51, Week52
/*add as many as you need*/)
)p
--ORDER BY
--1
option (maxrecursion 0)
Sample Data :
DECLARE #Products TABLE(Id INT PRIMARY KEY,
ProductName NVARCHAR(50))
DECLARE #Orders TABLE(ProductId INT,
DateofPurchase DATETIME,
Quantity BIGINT)
INSERT INTO #Products(Id,ProductName)
VALUES(1,N'Product1'),
(2,N'Product2')
INSERT INTO #Orders( ProductId ,DateofPurchase ,Quantity)
VALUES (1,'2018-01-01',130),
(1,'2018-01-09',140),
(1,'2018-01-16',150),
(1,'2018-01-24',160),
(2,'2018-01-01',30),
(2,'2018-01-09',40),
(2,'2018-01-16',50),
(2,'2018-01-24',60)
Query :
SELECT P.Id,
P.ProductName,
Orders.MonthName,
Orders.Week1,
Orders.Week2,
Orders.Week3,
Orders.Week4
FROM #Products AS P
INNER JOIN (SELECT O.ProductId,
SUM((CASE WHEN DATEPART(DAY,O.DateofPurchase) BETWEEN 1 AND 7 THEN O.Quantity ELSE 0 END)) AS Week1,
SUM((CASE WHEN DATEPART(DAY,O.DateofPurchase) BETWEEN 8 AND 15 THEN O.Quantity ELSE 0 END)) AS Week2,
SUM((CASE WHEN DATEPART(DAY,O.DateofPurchase) BETWEEN 16 AND 23 THEN O.Quantity ELSE 0 END)) AS Week3,
SUM((CASE WHEN DATEPART(DAY,O.DateofPurchase) >= 24 THEN O.Quantity ELSE 0 END)) AS Week4,
DATENAME(MONTH,O.DateofPurchase) AS MonthName
FROM #Orders AS O
GROUP BY O.ProductId,DATENAME(MONTH,O.DateofPurchase)) AS Orders ON P.Id = Orders.ProductId
Result :
-----------------------------------------------------------------------
| Id | ProductName | MonthNumber | Week1 | Week2 | Week3 | Week4 |
-----------------------------------------------------------------------
| 1 | Product1 | January | 130 | 140 | 150 | 160 |
| 2 | Product2 | January | 30 | 40 | 50 | 60 |
-----------------------------------------------------------------------

Generate random values from table

I'd like to generate random values in order to fill a table.
First, I have a city_table :
CREATE TABLE t_city_ci (
ci_id SERIAL PRIMARY KEY,
ci_name VARCHAR(100) NOT NULL
);
So I insert random values like this :
INSERT INTO t_city_ci ("ci_name")
SELECT DISTINCT(d.str)
FROM (
SELECT
(
SELECT string_agg(x, '') as str
FROM (
SELECT chr(ascii('A') + (random() * 25)::integer)
-- reference 'b' so it is correlated and re-evaluated
FROM generate_series(1, 10 + b * 0)
) AS y(x)
)
FROM generate_series(1,10000) as a(b)) as d;
Now, I have a temperature table that looks like this :
CREATE TABLE dw_core.t_temperatures_te (
te_id SERIAL PRIMARY KEY,
ci_id INTEGER,
te_temperature FLOAT NOT NULL,
te_date TIMESTAMP NOT NULL DEFAULT NOW()
);
How can I fill a temperature table with :
Random date from last year
Random temperature between -30 and 50
Random values from t_city table ?
I tried this but the date never changes :
INSERT INTO dw_core.t_temperatures_te ("ci_id","te_temperature","te_date")
SELECT *
FROM (
SELECT (random() * (SELECT MAX(ci_id) FROM dw_core.t_city_ci) + 1)::integer
-- reference 'b' so it is correlated and re-evaluated
FROM generate_series(1, 100000 )
) AS y
,(select random() * -60 + 45 FROM generate_series(1,1005)) d(f),
(select timestamp '2014-01-10 20:00:00' +
random() * (timestamp '2014-01-20 20:00:00' -
timestamp '2016-01-10 10:00:00')) dza(b)
LIMIT 1000000;
Thanks a lot
Something like this?
select * from (
select
(random() * 100000)::integer as ci_id,
-30 + (random() * 80) as temp,
'2014-01-01'::date + (random() * 365 * '1 day'::interval) as time_2014
from generate_series(1,1000000) s
) foo
inner join t_city_ci c on c.ci_id = foo.ci_id;
Here's a sample of the generated data:
select
(random() * 100000)::integer as ci_id,
-30 + (random() * 80) as temp,
'2014-01-01'::date + (random() * 365 * '1 day'::interval) as time_2014
from generate_series(1,10);
ci_id | temp | time_2014
-------+-------------------+----------------------------
84742 | 31.6278865475337 | 2014-10-16 21:36:45.371176
16390 | 10.665458049935 | 2014-11-13 19:59:54.148177
87067 | 43.2082599369847 | 2014-06-01 16:14:43.021094
25718 | -7.78245567240867 | 2014-07-23 05:53:10.036914
99538 | -5.82924078024423 | 2014-06-08 06:44:02.081918
71720 | 22.3102275898262 | 2014-06-15 08:24:00.327841
24740 | 4.65809369210996 | 2014-05-19 02:20:58.804213
56861 | -20.750980894431 | 2014-10-01 06:09:54.117367
47929 | -24.4018202994027 | 2014-11-24 13:39:54.096337
30772 | 46.7239395141247 | 2014-08-27 04:50:46.785239
(10 rows)

TSQL Insert additional rows

What is the most efficient way to identify and insert rows for the following problem?
Here's my sample data
vId StartDate EndDate Distance
------------------------------------
256 2015-03-04 2015-03-05 365
271 2015-03-04 2015-03-04 86
315 2015-03-05 2015-03-06 254
256 2015-03-07 2015-03-09 150
458 2015-03-10 2015-03-12 141
458 2015-03-15 2015-03-17 85
315 2015-03-15 2015-03-16 76
I want to add additional rows for each vId where the StartDate <> EndDate like follows, so instead of just
315 2015-03-05 2015-03-06 254
256 2015-03-07 2015-03-09 150
I want to show the following
315 2015-03-05 2015-03-06 254
315 2015-03-06 2015-03-06 0
256 2015-03-07 2015-03-09 150
256 2015-03-08 2015-03-09 0
256 2015-03-09 2015-03-09 0
Thanks in advance.
Just a simple insert:
Insert Into Table(vId, StartDate, EndDate, Distance)
Select vId, DateAdd(dd, 1, StartDate), EndDate, 0
From TableName
Where StartDate <> EndDate
If you want just select but not insert then:
Select vId, StartDate, EndDate, Distance
From TableName
Union All
Select vId, DateAdd(dd, 1, StartDate), EndDate, 0
From TableName
Where StartDate <> EndDate
EDIT
This assumes that there are maximum 100 day difference. If you have longer intervals you can add more cross joins to increase possible values:
declare #t table(vId int, StartDate date, EndDate date, Distance int)
insert into #t values
(315, '2015-03-05', '2015-03-06', 254),
(256, '2015-03-07', '2015-03-09', 150)
;with cte as(select row_number() over(order by (select 1)) as rn
from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t1(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t2(n)
)
select * from #t
union all
select t1.vId, ca.StartDate, t1.EndDate, 0
from #t t1
cross apply(select dateadd(dd, c.rn, StartDate) as StartDate
from cte c
where dateadd(dd, c.rn, t1.StartDate) <= t1.EndDate) as ca
where t1.StartDate <> t1.EndDate
order by vId, StartDate
See fiddle http://sqlfiddle.com/#!6/9eecb/4641