Distribute amount to different instances of balance column - sql

I have the following Customer table with columns id, name and balance.
I have amount to distribute to each of the rows till the amount becomes 0
it's like this
while(Amount>0)
begin
amount=(Amount)-(select the balance of the row)
(select the balance column of the row)=0
end
I do not want to use the loop or cursor either.
I used the case in the update query, but that is not working either.
declare #temp decimal(18,4)=1000
update Customer
set #temp=case
when #temp>Balance then
#temp-Balance
else #temp
end,
Balance=case
when Balance<=#temp then 0
else Balance
end
from person
where Balance<=#temp
select #temp

Build a running total (with sum(balance) over(order by balance)):
supplier balance total
A 300 300
B 500 800
C 1200 2000
D 1400 3400
All rows where total - balance < #amount must be updated (this is suppliers A, B, and C in the example above). The ones where total <= #amount get reduced to null; the one where total > #amount (supplier C in the example above) ends up with the difference of total - #amount.
update s
set balance = case when total <= #amount then 0 else total - #amount end
(
select
balance,
sum(balance) over (order by balance) as total
from suppliers
) s
where total - balance <= #amount;

Related

sql multiplying a sum

I'm struggling with the below code. i'm trying to work out the total balance of a folio by using its total charges and total payments. i'm almost there, however with the below code the payments is incorrect, it seems to be multiplying the total sum of payments by the number of entries in charges. i'm guessing this is because I've connected the payments and charges, which I needed to do to filter out the checkin date which is only on pms sales.
can anyone help?
thanks
SELECT DISTINCT 'PMS AD' AS APP,
FOLIO_ID,
SUM(TOTAL_CHARGES) as TOTAL_CHARGES,
SUM(TOTAL_PAYMENTS) as TOTAL_PAYMENTS
FROM ((SELECT DISTINCT P1.FOLIO_ID AS FOLIO_ID, SUM(P1.CHARGE_CODE_AMOUNT) AS TOTAL_CHARGES, 0 AS TOTAL_PAYMENTS
FROM DEV.VR_PMS_SALES P1
WHERE P1.CHARGE_CODE_AMOUNT <> 0 AND
P1.ITEM_OPERATING_DAY IS NOT NULL
AND P1.ITEM_OPERATING_DAY <= '03-DEC-2014'
AND P1.CHECKIN_DATE <= '03-DEC-2014'
GROUP BY P1.FOLIO_ID
) UNION ALL
(SELECT DISTINCT P2.FOLIO_ID AS FOLIO_ID, 0 AS TOTAL_CHARGES, SUM(P2.AMOUNT) AS TOTAL_PAYMENTS
FROM DEV.VR_PMS_PAYMENTS P2,
DEV.VR_PMS_SALES P3
WHERE P2.FOLIO_ID = P3.FOLIO_ID
AND P2.AMOUNT <> 0
AND P2.PMS_OPERATING_DAY <= '03-DEC-2014'
AND P3.CHECKIN_DATE <= '03-DEC-2014'
GROUP BY P2.FOLIO_ID
)
) F
GROUP BY FOLIO_ID
EDIT:
Sorry I didn't provide examples. table data below
VR_PMS_PAYMENTS
VR_PMS_SALES
The issue I am having is that when running the sql it is multiplying the sum of p1.amount by the number of entries in VR_PMS_SALES. eg folio 4 is returning as 165 instead of 55. I need it to return the below...
desired outcome
I hope this is clearer.
thank you

Account balance of more than 1000 over x days consecutively

Firstly, i have a transaction database with the following field. The requirement is to alert the admin of a user if the user holds usd 1000 in balance consecutively for more than 30 days.
TransactionID
TransactionType - deposit, withdrawal, transfer
TransactionFromUserID
TransactionToUserID
TransactionValueUsd
TransactionDateTime
Note:
- Currently i only have this table and do not have another table to update the balance. The balance is calculated on the fly.
If one of the days is not more than 1000 usd it is needed to be recalculate again
Need not worries about performance issue. Just need a general idea on how should i design another table to hold the value and maybe a trigger to solve this issue.
eg:
2019-01-01: deposit 500 usd
2019-02-01: deposit 2000 usd - balance 2500 usd, start count from here
2019-02-10: withdraw 2500 usd - balance 500, reset date
2019-02-15: deposit 2000 usd - balance 2500 - start exceed date again here
2019-04-15: withdraw 1000 usd - balance 1500 - flag here and reset last exceed date
Your question is not 100% clear, but I think this query is close enough to the answer:
select
*
from (
select
transactiontouserid,
transactiondatetime,
sum(case when balance < 1000 then 1 else 0 end)
over(
partition by transactiontouserid
order by transactiondatetime
range between interval '30' day preceding and current row
) as disqualify_points
from (
select
transactiontouserid,
transactiondatetime,
sum(case when transactiontype = 'deposit' then 1 else -1 end
* transactionvalueusd)
over(
partition by transactiontouserid
order by transactiondatetime
) as balance
from t -- your table name
) x
) y
where disqualify_points = 0

Grouping twice by different criteria, same column

I have data with the following columns:
OFFICER_ID, CLIENT_ID, SECURITY_CODE, POSITION_SIZE
and then per each row for instance:
officer1, client100, securityZYX, $100k,
officer2, client124, securityADF, $200k,
officer1, client130, securityARR, $150k,
officer4, client452, securityADF, $200k,
officer2, client124, securityARR, $500k,
officer7, client108, securityZYX, $223k,
and so on.
As you see, each client has a single officer assigned to either buy o sell securities, but each client can have bought different securities.
Apart from ranking officers by total amount in US$ of securities held by their clients (which I've done) I need to create ranges of total client accounts by adding total securities held by client ID, for example, total securities held sum < $1million, between $1million and $3million and > $3 million.
I've tried:
SELECT officer_ID, SUM(position_size) as AUM
FROM trades
GROUP BY client_ID
HAVING AUM > 1000000 AND AUM < 3000000;
and I get a list of all officers appearing several times, no totals.
I'd need a simple:
Officer_ID | range < 1m | range 1m-3m | range > 3m
officer1, [total amount of client accounts with securities adding up < 1m totals], [total amount of client accounts with securities adding up between 1m and 3m totals], etc.
Please, would you point me in the right direction?
UPDATE
I modified Tim's suggested code and obtained the desired output:
SELECT
OFFICER_ID,
SUM(CASE WHEN total < 1000000 THEN total END) AS "range < 1m",
SUM(CASE WHEN total >= 1000000 AND total < 3000000 THEN total END) AS "range 1m-3m",
SUM(CASE WHEN total >= 3000000 THEN total END) AS "range > 3m"
FROM
(
SELECT OFFICER_ID, CLIENT_ID, SUM(POSITION_SIZE) AS total
FROM trades
GROUP BY OFFICER_ID, CLIENT_ID
) t
GROUP BY
OFFICER_ID;
Too kind, Tim, thanks!
We can try aggregating twice, first by both officer and client, to get the client totals, and a second time by officer alone, to get the counts:
SELECT
OFFICER_ID,
COUNT(CASE WHEN total < 1000000 THEN 1 END) AS "range < 1m",
COUNT(CASE WHEN total >= 1000000 AND total < 3000000 THEN 1 END) AS "range 1m-3m",
COUNT(CASE WHEN total >= 3000000 THEN 1 END) AS "range > 3m"
FROM
(
SELECT OFFICER_ID, CLIENT_ID, SUM(POSITION_SIZE) AS total
FROM trades
GROUP BY OFFICER_ID, CLIENT_ID
) t
GROUP BY
OFFICER_ID;

Continous update variable value based on previous value

I am trying a update value of the sales column based on previously updated value. for example -
Table A
Day growth sales
1 1.1 1
2 1.2 NULL
3 1.3 NULL
I want to update the sales value based on growth.
So, for day 2 it would be day1 sales * growth.
and day 3 would be day 2 updated sales * growth.
Is this possible without a loop in netezza ?
Thanks everyone.
You can use a cumulative sum and logs to get a cumulative product:
select t.*,
(case when day = 1 then sales
else (exp(sum(ln(growth)) over (order by day) / growth) *
max(case when day = 1 then sales end) over ()
end) as new_sales
from t;
DECLARE #_i NUMERIC(18,2) = (SELECT Sales FROM A WHERE Day = '1')
UPDATE A
SET #_i = Sales = #_i * CASE WHEN _Day = '1' THEN (SELECT Sales FROM A WHERE Day = '1')
ELSE GROWTH END
SELECT * FROM A
Here is my answer, try this

Calculate days from the last time a customer has zero balance

I have a Table with these columns :
TransID, CustomerID, Date, Credit, Debit, CurrentBalance
I want to know how many days have passed since the customer had a clear balance, because I don't give credit if they have not cleared their balance in the last 14 days,
let's speak about a specific customer:
TransID, CustomerID, Date, Credit, Debit, CurrentBalance
1 1 01/01/2014 0 50 50
2 1 01/05/2014 50 0 0
3 1 06/28/2014 0 100 100
Now on 6/29/14 they have only 1 day since their balance was clear, but if I calculate from the last row with CurrentBalance = 0, it is more than 175 days
Logically, the last time the balance was zero was the instant before the last sale was made when the balance was zero, which can be identified by the sale amount equalling the balance after the sale, ie Debit = CurrentBalance - this confition can only happen when the balance before the sale was zero.
select
c.id customerid,
coalesce(datediff(day, max(t.date), getdate()), 0) days_clear
from customer c
left join transaction t on t.CustomerID = c.id
and Debit = CurrentBalance
group by customerid
Using the customer table and a left join to the transaction table allows for the case when customer has never made a transaction (so his day count is zero).
Is this what you are looking for?
select customerid,
datediff(day, max(case when balance = 0 then date end), getdate())
from table t
group by customerid;
This returns the number of days since the most recent 0 balance record.
EDIT:
Now I think I understand. The problem is that the 0 balance lasts until 6/18/2014. With SQL Server 2012 or later, we can handle this with lead():
select customerid,
datediff(day, max(case when balance = 0 then nextdate end), getdate())
from (select t.*, lead(date) over (partition by customerid order by date) as nextdate
from table t
) t
group by customerid;
T