How to differentiate between debit and credit account in SQL Server columns? - sql

Here is some sample data:
Transaction_ID
Amount
Credit_Account
Debit_Account
Transaction_Type
TT23023F02B9
200
PKR1000090662010
135483688
Cash deposit
TT23023FX3MZ
1658297
PKR1000023233010
PKR1000099993010
Teller to Teller Transfer
TT23023GPNPS
260000
133519319
PKR1000073160016
Cash Withdrawal
TT23023HLP3P
342500
135482722
PKR1000015443000
Cash Withdrawal
TT23023JGVTC
2046980
PKR1000032342023
PKR1000099992023
Teller to Teller Transfer
TT23023KGGPW
857500
135484137
PKR1000050214016
Cash Withdrawal
TT23023LF6J6
274700
132352909
PKR1000031072030
Cash Withdrawal
TT23023MC2FX
263926
PKR1000051764012
PKR1000099994012
Teller to Teller Transfer
TT23023N37H0
179000
PKR1000031353007
132620326
Cash deposit
TT23023P067S
6400
PKR1000014553002
131623003
Cash deposit
TT23023PZM84
1561200
135478636
PKR1000052572024
Cash Withdrawal
FT2302348TXC
147542
120673138
128787728
AA Loan Payoff
Most of the columns are self-explanatory but let explain a little.
There are some transactions going on with unique Transaction_ID.
A user can have multiple accounts, so, Credit_Account and Debit_Account aren't unique for a user(Transaction_ID).
Debit_Account is the account from which the Amount is transferred while Credit_Account is the receiving account.
I want to calculate Total_Credit_Amount and Total_Debit_Amount with respect to Accounts in both columns.
Distinct Credt/Debit Acc
Total_Credit_Amount
Total_Debit_Amount
abc
amount
amount
xyz
amount
amount
I tried different iterations of CASE statements but all in vain.

You can use two subqueries combined and then summarised, as follows:
select
Account
, sum(Credit_Amount) as Credit_amount
, sum(Debit_Amount) as Debit_amount
from (
select
Credit_Account as Account
, Amount as Credit_Amount
, 0 as Debit_Amount
from SomeData
union all
select
Debit_Account as Account
, 0 as Credit_Amount
, Amount as Debit_Amount
from SomeData
) Step1
group by Account
Alternative not relying on union's column order:
select
Account
, sum(case when CR_DR='CR' then Amount else 0 end) as Credit_amount
, sum(case when CR_DR='DR' then Amount else 0 end) as Debit_amount
from (
select
Credit_Account as Account
, CR_DR='CR'
, Amount as Amount
from SomeData
union all
select
Debit_Account as Account
, CR_DR='DR'
, Amount as Amount
from SomeData
) Step1
group by Account
Alternative using PIVOT command, based on #June7's idea (thank you):
select
Account
, Credit_Amount
, Debit_Amount
from (
select
Credit_Account as Account
, CR_DR='Credit_Amount'
, Amount as Amount
from SomeData
union all
select
Debit_Account as Account
, CR_DR='Debit_Amount'
, Amount as Amount
from SomeData
) Step1
pivot (
SUM(amount)
FOR CR_DR IN([Credit_Amount],[Debit_Amount])
) as PT

Related

SQL Running Total Using Two Columns

I have the following sample dataset
ID
Type
Opening Balance
Trans Amount
1
Credit
1000.00
40.00
2
Debit
1000.00
-50.00
3
Debit
1000.00
-20.00
4
Credit
1000.00
10.00
5
Debit
1000.00
-30.00
The opening balance is a fixed amount from the previous day. What i want is to use it to start computing the running balance from the first row. The desired output should be as follows
ID
Type
Opening Balance
Trans Amount
Running Total
1
Credit
1000.00
40.00
1040.00
2
Debit
1000.00
-50.00
990.00
3
Debit
1000.00
-20.00
970.00
4
Credit
1000.00
10.00
980.00
5
Debit
1000.00
-30.00
950.00
The script i have written so far is only able to do a running total on the trans amount without factoring the opening balance on the first row
SELECT SUM([trans amount]) OVER (PARTITION BY id ORDER BY id) from dbo.table1
How do i achieve the intended results
I was originally going the same direction as lemon's answer, but figured a way that you only add [Opening Balance] from the first row would be useful as well.
You can make a CTE with a case that will add the first row to get your new total, and then the rest of the original values, and then sum that new column:
with tbl as
(
select ID
, Type
, [Opening Balance]
, [Trans Amount]
, case
when ROW_NUMBER() over (order by ID) = 1 then [Opening Balance] + sum([Trans Amount]) over (order by ID)
else [Trans Amount]
end [New Trans Amount]
from orig_tbl
)
select ID
, Type
, [Opening Balance]
, sum([New Trans Amount]) over (order by ID) [Running Total]
from tbl
What's nice about this way is that your subsequent opening balance can vary without affecting [Running Total].
However, if [Opening Balance] does not vary, then lemon's version is simpler.
It is sufficient to add up the Opening Balance value to your window function result:
SELECT *,
[Opening Balance] + SUM([Trans Amount]) OVER(ORDER BY [ID]) AS [Running Total]
FROM table1
Partitioning inside the window function is not necessary as long as you have unique values for the ID field (at least in the input sample).
Try it here.
Note: I have assumed you're using SQL Server by looking at your query. If that's not the case, please indicate it into your post as a tag.

How do I join these 3 tables, do a sum calculation and group by multiple values?

So I have these 3 tables below:
User Table:
Deposit Table:
Withdrawal Table:
Question:
How do I join these 3 tables together, having the values grouped by:
Month
Sex
Age
State
Expected Output:
As seen from my expected output above, I want to separate these users into different buckets
i.e.
Month: January, Sex: F, Age: 18, State: Arizona (Bucket 1)
Month: January, Sex: M, Age: 21, State: Texas (Bucket 2)
and find the sum(Deposit.amount) & sum (Withdrawal.amount) group by the fields above ^
as well as how many users are in this bucket.
Clarification:
"Month" column in my Expected Ouput is based on Transaction Month for both Deposit & Withdrawal table combined, I just added the "Account Created Month" column in User table just in case its possible to do a 3-table join through Month values.
This should work:
SELECT tr.Month, u.Sex, u.Age, u.Stage
, Sum(CASE WHEN [Type] = 'D' THEN tr.Amount ELSE 0 END) As [Total Deposit Amount]
, Sum(CASE WHEN [Type] = 'W' THEN tr.Amount ELSE 0 END) As [Total Withdrawal Amount]
, COUNT(distint u.UserID) As [Number of Users in this Category]
FROM (
SELECT userid, [Deposit Amount] As Amount, [Transaction Month] As Month, 'D' As [Type] FROM [Deposit]
UNION ALL
SELECT userid, [Withdrawal Amount], [Transaction Month], 'W' FROM [Withdrawal]
) tr
INNER JOIN User u ON u.UserID = tr.UserId
GROUP BY tr.Month, u.Sex, u.Age, u.State
Really, deposits and withdrawals should be the same table (Transactions) to begin with, where the only difference is withdrawals are negative.
Also... please stop using images to represent your sample data and results. It makes things so much harder on us to help you, which makes it less likely you'll get a good or prompt answer.

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

Join up two rows to one in Oracle Sql at a particular column

I have a table that stores transactions if this fomat
Tran_date tran_id account_number tran_type amount
21-07-2017 M23 1234567890 D 50000
21-07-2017 M23 0987654321 C 50000
21-07-2017 M24 7654328900 D 20000
21-07-2017 M24 8845874588 C 20000
these are debit and credit for two transactions. I am required to report this in one row for every transaction like this:
Tran_date tran_id src_acct dest_acct Amount
21-07-2017 M23 1234567890 0987654321 50000
21-07-2017 M24 7654328900 8845874588 20000
I have tried to select max of account Number grouping by account number but I am still getting individual rows. How do I go about this?
You can use conditional aggregation for this:
SELECT Tran_date, tran_id, MAX(Amount) AS Amount
MAX(CASE WHEN tran_type = 'D' THEN account_number END) AS src_acct,
MAX(CASE WHEN tran_type = 'C' THEN account_number END) AS dest_acct
FROM mytable
GROUP BY Tran_date, tran_id
The query assumes the amount is the same between 'D' and 'C' records.
Join will do also. Assuming (tran_id, tran_type) is unique
SELECT td.Tran_date, td.tran_id, td.account_number as src_acct, tc.account_number as dest_acct, td.Amount
FROM mytable td
JOIN mytable tc ON td.tran_type = 'D' AND tc.tran_type = 'C'AND tc.tran_id = td.tran_id;

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