SQL Running Total Using Two Columns - sql

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.

Related

Query to Sum values from one table based on values from another table, considering null values

I have two tables in MS Access. One for contracts, with contract id and total value. Another one for the payments made in each contract.
So for example when a client gets a new contract for a total value of USD 100.00 then he can pay it in any amount he want up to the total value, like two payments of 50.00 or one of 30.00 and another of 70.00 or even one hundred payments of 1.00.
And those payments go to the payments table with the contract id and the payment amount.
I need to make a query to find how much do they still have to pay for each contract. What is the net amount for each contract.
I managed to make a query that gives me the result I need, but if the contract is new and there are no payments on the payments table for this new contract, the net amount shown in my query is zero (blank field) and I needed it to show the total value of the contract instead.
For example
Contract of 100.00 with two payments of 10.00 and 15.00 should show the net value as 75.00
Contract of 100.00 with no payments should show net value as 100.00
Here is what I have so far:
'Query 1
SELECT Contracts.[Contract ID],
Sum(Payments.[Payment Value]
FROM Contracts
LEFT JOIN Payments
ON Contracts.[Contract ID] = Payments.[Contract ID]
GROUP BY Contracts.[Contract ID]
Query 2:
SELECT Contracts.[Contract ID],
Contracts.[Contract Value],
Query1.[SumOfValue Payment Value],
[Contract Value] - [SumOfValue Payment Value]
INTO NetValues
FROM Contracts
INNER JOIN Query1
ON Contracts.[Contract ID] = Query1.[Contract ID]
Basically the Query 1 , sums the payments for each contract and Query 2 calculates the net value (total value - paid amount)
If there is any better way to achieve all of this please let me know.
You can do it as a single query as follows:
SELECT c.[Contract ID],
c.[contract value],
p.Paid,
c.[contract value]-nz(p.paid) AS Remaining
FROM Contracts AS c
LEFT JOIN
(SELECT [Contract ID], sum([Payment Value]) as paid
FROM Payments
group by [Contract ID]) AS p
ON c.[Contract ID] = P.[Contract ID];
Given these two tables:
The query will produce this result:
" ... if the contract is new and there are no payments on the payments table for this new contract, the net amount shown in my query is zero (blank field)"
Actually, when you have no payments recorded for a given contract, Query1 gives you Null, not zero, as the sum of the payments. And any number minus Null gives you Null, not that first number.
If you want the Null treated as zero, use the
Nz Function
Here's an Immediate window example of those issues:
Contract_Value = 100.00
SumOfValue_Payment_Value = Null
? Contract_Value - SumOfValue_Payment_Value
Null
' use Nz() to substitute zero for Null
? Contract_Value - Nz(SumOfValue_Payment_Value, 0)
100
You can do the same in Query2 by including this:
[Contract Value] - Nz([SumOfValue Payment Value], 0)

Cumulative tiered rate calculation in SQL Server for DML UPDATE/INSERT trigger?

Essentially, using SQL Server, I want to take the "Gross Amt" from the current table below (which is derived from a computed column upon INSERT or UPDATE) and then have that "Gross Amt" run through the "Tiered Table" to derive the "Total A $" in the desired output table.
I figured this would likely need to be done with a trigger (maybe a function?) since this calculation would happen upon INSERT or UPDATE and because the conditional logic could be incorporated into it since there are different tier tables with different Min/Max values and percentage thresholds for different tiers.
The example below is, of course, cumulative, and functions like marginal income tax rates, the first 10000 is at 90% (for Total A), the second tier calculates the 19999 at 60%, the third 69999 at 40%, and so on, etc. There are other regions with different tiers that are just simple lookup reference values.
Tiered table:
RegionID
TierNo
Min
Max
Total A
Total B
3
1
0
10000
.90
.10
3
2
10001
30000
.60
.40
3
3
30001
100000
.40
.60
3
4
100001
500000
.40
.60
3
5
500001
999999999999
.20
.80
Current table sample:
TransID
RegionID
GrossAmt
Total A %
Total A $
Net Amt
100001
3
125000
Desired output:
TransID
RegionID
GrossAmt
Total A %
Total A $
Net Amt
100001
3
125000
0.47
59000
66000
Any ideas or guidance would be extremely helpful and appreciated.
Firstly, your tiers aren't quite right. For example, the first one begins at 0 and ends at 10000, but the next one starts at 10001, leaving anything between 10000 and 10001 unaccounted for. Instead, have your tiers abutting each other, and pick your intervals carefully using > AND <=.
Secondly, this doesn't need a trigger at all. You should just calculate this on the fly when you need it. Create a view or an inline Table Valued Function if you need to.
It looks like a fairly simple grouped join, which you can do neatly using CROSS APPLY. You just need to calculate how much to multiply for each tier: the lower of GrossAmt or Max, subtracting Min
SELECT
c.*,
[Total A %] = t.Total / c.GrossAmt,
[Total A $] = t.Total,
[Net Amt] = c.GrossAmt - t.Total
FROM CurrentData c
CROSS APPLY (
SELECT
Total = SUM((v.ActualMax - t.Min) * t.[Total A])
FROM Tiers t
CROSS APPLY (VALUES(
CASE WHEN c.GrossAmt < t.Max THEN c.GrossAmt ELSE t.Max END
)) v(ActualMax)
WHERE c.GrossAmt > t.Min
) t;
db<>fiddle
You can do this as an inline Table Valued Function if you want.
CREATE FUNCTION dbo.GetTieredTotal (#GrossAmt decimal(18,9))
RETURNS TABLE
AS RETURN
SELECT
Total = SUM((CASE WHEN #GrossAmt < t.Max THEN #GrossAmt ELSE t.Max END - t.Min) * t.[Total A])
FROM Tiers t
WHERE #GrossAmt > t.Min
;
You can then change the main query to
SELECT
c.*,
[Total A %] = t.Total / c.GrossAmt,
[Total A $] = t.Total,
[Net Amt] = c.GrossAmt - t.Total
FROM CurrentData c
CROSS APPLY dbo.GetTieredTotal (c.GrossAmt) t;

How do you combine rows in a group that have a certain condition? [duplicate]

This question already has an answer here:
How to combine rows for positive amount in a group [closed]
(1 answer)
Closed 1 year ago.
I need to generate a report that shows transactions with 2 or more Mode Of Payments and with change. The strange thing about this is these are transactions that used cards and e-wallets as MOPs but were still given change. What I'm having difficulty in is combining the total amount paid in a transaction, leaving the row that shows the amount of change given. To illustrate, say this is the table:
Receipt No.
MOP
Amount
0001
Cash
100
0001
Cash
-70
0001
Card
500
0002
Cash
-50
0002
E-Wallet
300
0002
Card
250
0003
Cash
-100
0003
Cash
150
0003
Card
200
I want to produce something like this
Receipt No.
MOP
Amount
0001
Cash
-70
0001
Cash, Card
600
0002
Cash
-50
0002
E-Wallet, Card
550
0003
Cash
-100
0003
Card
350
Reposted this for clarity. I just started learning and using SQL recently so any help would be appreciated. Thank you!
I guess you wanted to identify any abnormality in the transactions
The CTE trans identifies those transactions with more than one MOP and with Amount negative.
After that, join to the original table, GROUP BY SIGN(Amount) to segregate positive Amount from negative
string_agg() is used to concatenate the MOP into comma delimited
with trans as
(
select receipt_no
from transactions
group by receipt_no
having count(distinct MOP) > 1
and min(Amount) < 0
)
select r.receipt_no,
string_agg(MOP, ',') as MOP,
sum(t.Amount) as Amount
from trans r
inner join transactions t on r.receipt_no = t.receipt_no
group by r.receipt_no, sign(t.Amount)
demo
I need to generate a report that shows transactions with 2 or more
Mode Of Payments and with change
You should also explain that with change are those line with negative Amount. Not everybody understand your context.
Here's a simple idea to serve as a tarting point for you (considering SQL Server or SQL Azure database syntax and functions):
SELECT
[Receipt No.],
STRING_AGG([MOP], ',') AS [MOPs],
SUM([Amount]) AS TotalAmount,
FROM (
SELECT
[Receipt No.],
[MOP],
[Amount],
CASE WHEN [Amount] < 0 THEN 'Change' ELSE 'Payment' END AS [Type]
FROM TABLE
) A
GROUP BY [Receipt No.], [Type]
ORDER BY 1;

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

SQL Pivot and Grand total?

I have a pivot table and I would like to know how to add an extra row that will be the grand total, for example,
Christmas, Hallowen
500 , 800
600 , 700
Total 1100 , 1500
Anything like this would help a lot, below is the code I've used to create my pivot table.
/*Pivot table */
SELECT * FROM
(SELECT Phase2.[Ref]
,Phase2.[Parties]
,Phase2.[TypeOfParty]
,Phase2.[Cost]
,Phase2.[DressCode]
,0 + SUM(Phase2.Cost) OVER (ORDER BY [REF]) AS 'Running Total'
FROM Stage2) AS BaseData
PIVOT(
SUM(BaseData.[Cost])
FOR BaseData.[Parties]
IN([Christmas], [Halloween])
) AS PivotTable
The Line below does give the running cost per Party but it would be good to have it as a new row and not as a column, looks a lot better.
,0 + SUM(Phase2.Cost) OVER (ORDER BY [REF]) AS 'Running Total'
Currently my results with the current pivot above are:
ref TypeOfParty, DressCode Christmas, Halloween, Running Total
1 Christmas, XmasWear, 500 , 500
2 christmas, XmasWear, 500 , , 1000
3 Halloween, HallWear, , 400 , 400
4 Halloween, HallWear, , 300 , 700
If anyone needs me to clarify something, I'm here. Hope there's enough information for you all to understand what I'm trying to do.
Does this do what you want?
SELECT s.Ref, s.TypeOfParty, s.DressCode,
SUM(CASE WHEN Parties = 'Christmas' THEN s.cost ELSE 0 END) as Christmas,
SUM(CASE WHEN Parties = 'Halloween' THEN s.cost ELSE 0 END) as Halloween
FROM Stage2 s
GROUP BY GROUPING SETS ( (s.ref), () );
This will include an overall total row in the result set.
If you want running totals, you can include a separate column for each party (one column doesn't make sense with a total row):
SUM(SUM(CASE WHEN Parties = 'Christmas' THEN s.cost ELSE 0 END)) OVER (ORDER BY s.ref) as Christmas,