Account balance of more than 1000 over x days consecutively - sql

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

Related

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

Distribute amount to different instances of balance column

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;

Query to find and average weighted price for day trades

I found this old question which brings a nice approach for calculating weighted average prices. It basically consists in grouping by the stock name and then fetching the sum(quantity*price)/sum(quantity)
But in day trades you buy and sell the asset in the same day, meaning that the final quantity of the day is zero and sql returns: Divide by zero error encountered
A examples would be
3 trades for the same stock
1. Price 10 Quantity
2. 100 Price 8 Quantity 100
3. Price 30 Quantity 200
Do you guys know some workaround ? Is there a way to group trades with positive and negative quantities separately ?
sure, add a grouping bucket defined by the sign of the amount...
Select assetIdentifier,
case when amount > 0 then 'debit' else 'credit' end typeTx,
Avg(Amount)
from table
group by assetIdentifier,
case when amount > 0 then 'debit' else 'credit' end
or, if you want both values on a single output row,
Select assetIdentifier,
avg(case when amount > 0 then amount end) debit ,
avg(case when amount < 0 then amount end) credit
from table
group by assetIdentifier
The formula for the weighted average is:
sum(quantity*price)/sum(quantity)
------------------------^ NOT price
If you want to ignore the direction of the trade, then just use absolute value:
sum(abs(quantity)*price)/sum(abs(quantity))

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