Retain values till there is a change in value in Teradata - sql

There is a transaction history table in teradata where balance gets changed only when there is a transaction
Data as below:
Cust_id Balance Txn_dt
123 1000 27MAY2018
123 350 31MAY2018
For eg,For a customer(123) on May 27 we have a balance of 1000 and on May 31 there is a transaction made by the customer so balance becomes 350. There is no record maintained for May 28 to May 30 with same balance as on May 27 . I want these days data also to be there (With same balance retained and the date is incremented ) Its like same record has to be retained for rest of the days till there is a change in a balance done by the transaction . How to do this in teradata?
Expected output:
Cust_id Balance Txn_dt
123 1000 27MAY2018
123 1000 28MAY2018
123 1000 29MAY2018
123 1000 30MAY2018
123 350 31MAY2018
Thanks
Sandy
Hi Dnoeth. It seems to work, but can you let me know how to expand till a certain day for eg : till 30JUN2018 ?

There are several ways to get this result, the simplest in Teradata utilizes Time Series Expansion for Periods:
WITH cte AS
(
SELECT Cust_id, Balance, Txn_dt,
-- return the next row's date
Coalesce(Min(Txn_dt)
Over (PARTITION BY Cust_id
ORDER BY Txn_dt
ROWS BETWEEN 1 Following AND 1 Following)
,Txn_dt+1) AS next_Txn_dt
FROM tab
)
SELECT Cust_id, Balance
,Last(pd) -- last day of the period
FROM cte
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(Txn_dt, next_Txn_dt) AS pd
If you run TD16.10+ you can replace the MIN OVER with a simplified LEAD:
Lead(Txn_dt)
Over (PARTITION BY Cust_id
ORDER BY Txn_dt)

Related

sharing cash with priority to creditors in sql

I have a table in sql 2014 with name "tblPaymentPlan" like this:
Creditors PlanToPay َAmount
----------------------------------
A 2017-01-20 2000
A 2017-02-20 1500
A 2017-03-20 3000
B 2017-01-25 3000
B 2017-02-25 1000
and also another table with name "tblPaid" like following:
Creditors Paid َ
-----------------
A 4500
B 3500
and the result that I expect:
Creditors PlanToPay َRemain
----------------------------------
A 2017-01-20 0
A 2017-02-20 0
A 2017-03-20 2000
B 2017-01-25 0
B 2017-02-25 500
I have no idea for doing this job at all! Would you please to help me to perform this job. Please informed that I have a lot of records in my tables.
I need this query for budget planing. (We can use numbers for defining priority instead of dates)
What you want is a running total of what is owing, from that you can subtract what has been paid.
SELECT Creditors, PlanToPay, IIF(ABS(Remain)!=Remain,0,IIF(Remain<Amount,Remain,Amount)) as Remain
FROM (SELECT pp.Creditors, pp.PlanToPay, pp.Amount,
SUM(pp.Amount) OVER(PARTITION BY pp.Creditors ORDER BY pp.PlanToPay ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)-tp.paid AS Remain
FROM tblPaymentPlan pp
JOIN (SELECT creditors, sum(paid) as paid from tblpaid group by creditors) tp
ON pp.creditors = tp.creditors) ss
ORDER By Creditors, PlanToPay
SQLFiddle
In the windowing function (SUM OVER) the PARTITION separates the creditors, the ORDER determines how the rows are arranged (by date), and the ROWS clause tells it to use all the rows in the partition before this row and include this row in the running total. We then subtract the sum of everything paid to that creditor from this running total.
This of course gives us alot of negative numbers, so we do it in a subquery. The main query checks if the absolute value of that remaining is equal to the value, true if it's positive, false if it is not, and returns the value remaining if true, or 0 if not.
UPDATE - added handling for multiple rows with value still owing
You can subtract the running total from amount in paid table and if it is less than 0, set remain to 0 else the difference of amount from the running total.
select pp.creditors,pp.plantopay,
case when sum(pp.amount) over(partition by pp.creditors order by pp.plantopay)-coalesce(pd.paid,0) <= 0 then 0
else sum(pp.amount) over(partition by pp.creditors order by pp.plantopay)-coalesce(pd.paid,0) end as remain
from tblpaymentplan pp
left join tblPaid pd on pp.creditors=pd.creditors

How can I make this query recursive Sql Server?

I have this table structure for Balances table:
And this is the view:
I have also this structure for Amounts table:
This is the view mode for Amounts table:
First of all I need to get the amount value for a specific day in Amounts Table:
with this query I get the amount 300 in date 07/07/2016.
Once achieved this figure, I need to make a recursive query with Balances table.
The end result should be like this:
Name abstractAmount addAmount Balance
----- -------------- --------- -------
Josep 100 400
Maria 50 350
George 60 410
Julianne 25 385
what is this? This result is achieved taking the 300 from the Amounts table, and for each row in Balance table I see:
If the abstracAmount in the first row is not empty, I make this mathematical calculation: balance = (300 - abstractAmount), in case is empty and the addAmount column has values I make this mathematical calculation balance = (300 + addAmount)
In the rest of rows I do the same but the calculation is not on 300, is on the last row balance:
For example:
In the first row the balance is 400 because the addamount has value so I make this calculation : 300 + 100 = 400
In the second row the balance is 350 because the abstractAmount is not empty so I take the balance value for the last row and make this calculation : 400 - 50 = 350.
And the same thing for the rest of rows, only the first row takes the balance value for the amounts table.
Notes:
1. Always the column abstractAmount subtracts values, and the addAmount column sum values.
Always one of this columns (abstractAmount | addAmount) will be empty .
Only the first row takes the value to make the mathematical calculation for the Amounts table, the rest of rows takes the value for the row before.
How can I get this final result? :
Name abstractAmount addAmount Balance
----- -------------- --------- -------
Josep 100 400
Maria 50 350
George 60 410
Julianne 25 385
I accept suggestions, thanks.
Instead of recursion, you can use window functions. More specifically a sum over rows unbounded preceding to get a running total (+ the start balance):
select *,300 + sum(isnull(addAmount,0) - ISNULL(abstractAmount,0)) over (order by id rows unbounded preceding) Balance
from Balances
The isnull(addAmount,0) - ISNULL(abstractAmount,0) is simply the mutation for every row. The over (order by id rows unbounded preceding) scopes the sum to the current row and all preceding rows according to id.
To get the base from the amounts table, you can have simply have the (select ...where date..) as a value instead of '300' or a bit more nifty: with a cross join to the amounts table:
select b.*, a.dateInsertion,a.amount, a.amount + sum(isnull(addAmount,0) - ISNULL(abstractAmount,0)) over (order by b.id rows unbounded preceding) Balance
from Balances b
cross join Amounts a
where a.dateInsertion = '20160707'
With the cross join without the where, you would get all possible balances

Performing math on SELECT result rows

I have a table that houses customer balances and I need to be able to see when accounts figures have dropped by a certain percentage over the previous month's balance per account.
My output consists of an account id, year_month combination code, and the month ending balance. So I want to see if February's balance dropped by X% from January's, and if January's dropped by the same % from December. If it did drop then I would like to be able to see what year_month code it dropped in, and yes I could have 1 account with multiple drops and I hope to see that.
Anyone have an ideas on how to perform this within SQL?
EDIT: Adding some sample data as requested. On the table I am looking at I have year_month as a column, but I do have access to get the last business day date per month as well
account_id | year_month | ending balance
1 | 2016-1 | 50000
1 | 2016-2 | 40000
1 | 2016-3 | 25
Output that I would like to see is the year_month code when the ending balance has at least a 50% decline from the previous month.
First I would recommend making Year_Month a yyyy-mm-dd format date for this calculation. Then take the current table and join it to itself, but the date that you join on will be the prior month. Then perform your calculation in the select. So you could do something like this below.
SELECT x.*,
x.EndingBalance - y.EndingBalance
FROM Balances x
INNER JOIN Balances y ON x.AccountID = y.AccountID
and x.YearMonth = DATEADD(month, DATEDIFF(month, 0, x.YearMonth) - 1, 0)

oracle sql: efficient way to calculate business days in a month

I have a pretty huge table with columns dates, account, amount, etc. eg.
date account amount
4/1/2014 XXXXX1 80
4/1/2014 XXXXX1 20
4/2/2014 XXXXX1 840
4/3/2014 XXXXX1 120
4/1/2014 XXXXX2 130
4/3/2014 XXXXX2 300
...........
(I have 40 months' worth of daily data and multiple accounts.)
The final output I want is the average amount of each account each month. Since there may or may not be record for any account on a single day, and I have a seperate table of holidays from 2011~2014, I am summing up the amount of each account within a month and dividing it by the number of business days of that month. Notice that there is very likely to be record(s) on weekends/holidays, so I need to exclude them from calculation. Also, I want to have a record for each of the date available in the original table. eg.
date account amount
4/1/2014 XXXXX1 48 ((80+20+840+120)/22)
4/2/2014 XXXXX1 48
4/3/2014 XXXXX1 48
4/1/2014 XXXXX2 19 ((130+300)/22)
4/3/2014 XXXXX2 19
...........
(Suppose the above is the only data I have for Apr-2014.)
I am able to do this in a hacky and slow way, but as I need to join this process with other subqueries, I really need to optimize this query. My current code looks like:
<!-- language: lang-sql -->
select
date,
account,
sum(amount/days_mon) over (partition by last_day(date))
from(
select
date,
-- there are more calculation to get the account numbers,
-- so this subquery is necessary
account,
amount,
-- this is a list of month-end dates that the number of
-- business days in that month is 19. similar below.
case when last_day(date) in ('','',...,'') then 19
when last_day(date) in ('','',...,'') then 20
when last_day(date) in ('','',...,'') then 21
when last_day(date) in ('','',...,'') then 22
when last_day(date) in ('','',...,'') then 23
end as days_mon
from mytable tb
inner join lookup_businessday_list busi
on tb.date = busi.date)
So how can I perform the above purpose efficiently? Thank you!
This approach uses sub-query factoring - what other RDBMS flavours call common table expressions. The attraction here is that we can pass the output from one CTE as input to another. Find out more.
The first CTE generates a list of dates in a given month (you can extend this over any range you like).
The second CTE uses an anti-join on the first to filter out dates which are holidays and also dates which aren't weekdays. Note that Day Number varies depending according to the NLS_TERRITORY setting; in my realm the weekend is days 6 and 7 but SQL Fiddle is American so there it is 1 and 7.
with dates as ( select date '2014-04-01' + ( level - 1) as d
from dual
connect by level <= 30 )
, bdays as ( select d
, count(d) over () tot_d
from dates
left join holidays
on dates.d = holidays.hol_date
where holidays.hol_date is null
and to_number(to_char(dates.d, 'D')) between 2 and 6
)
select yt.account
, yt.txn_date
, sum(yt.amount) over (partition by yt.account, trunc(yt.txn_date,'MM'))
/tot_d as avg_amt
from your_table yt
join bdays
on bdays.d = yt.txn_date
order by yt.account
, yt.txn_date
/
I haven't rounded the average amount.
You have 40 month of data, this data should be very stable.
I will assume that you have a cold body (big and stable easily definable range of data) and hot tail (small and active part).
Next, I would like to define a minimal period. It is a data range that is a smallest interval interesting for Business.
It might be year, month, day, hour, etc. Do you expect to get questions like "what was averege for that account between 1900 and 12am yesterday?".
I will assume that the answer is DAY.
Then,
I will calculate sum(amount) and count() for every account for every DAY of cold body.
I will not create a dummy records, if particular account had no activity on some day.
and I will save day, account, total amount, count in a TABLE.
if there are modifications later to the cold body, you delete and reload affected day from that table.
For hot tail there might be multiple strategies:
Do the same as above (same process, clear to support)
always calculate on a fly
use materialized view as an averege between 1 and 2.
Cold body table totalc could also be implemented as materialized view, but if data never change - no need to rebuild it.
With this you go from (number of account) x (number of transactions per day) x (number of days) to (number of account)x(number of active days) number of records.
That should speed up all following calculations.

sql DB calculation moving summary‏‏‏‏‏

I would like to calculate moving summary‏‏‏‏‏:
Total amount:100
first receipt: 20
second receipt: 10
the first row in calculation column is a difference between total amount and the first receipt: 100-20=80
the second row in calculation column is a difference between the first calculated_row and the first receip: 80-10=70
The presentation is supposed to present receipt_amount, balance:
receipt_amount | balance
20 | 80
10 | 70
I'll be glad to use your help
Thanks :-)
You didn't really give us much information about your tables and how they are structured.
I'm assuming that there is an orders table that contains the total_amount and a receipt_table that contains each receipt (as a positive value):
As you also didn't specify your DBMS, this is ANSI SQL:
select sum(amount) over (order by receipt_nr) as running_sum
from (
select total_amount as amount
from orders
where order_no = 1
union all
select -1 * receipt_amount
from the_receipt_table
where order_no =
) t
First of all- thanks for your response.
I work with Cache DB which can be used both SQL and ORACLE syntax.
Basically, the data is locaed in two different tables, but I have them in one join query.
Couple of rows with different receipt amounts and each row (receipt) has the same total amount.
Foe example:
Receipt_no Receipt_amount Total_amount Balance
1 20 100 80
1 10 100 70
1 30 100 40
2 20 50 30
2 10 50 20
So, the calculation is supposed to be in a way that in the first receipt the difference calculation is made from the total_amount and all other receipts (in the same receipt_no) are being reduced from the balance
Thanks!