CASE WHEN to calculate tax on a tier based tax system - sql-server

I have a table with user incomes and i wish to calculate their income tax percentage based on that income. The issue is that the tax rate is different for each bracket e.g.:
MinLimit| MaxLimit| TaxRate
0 | 14000 | 10.50
14001 | 48000 | 17.50
48001 | 70000 | 30.00
70001 | 1000000 | 33.00
So if the income of 1 person is 49,000 then they would be taxed as follows:
14000 * 0.1050 = 1470
34000 * 0.1750 = 5950 (34,000 is income between 14k -48k)
1000 * 0.30 = 300 (1000 is remaining income)
total = 1470 + 5950 + 300 = 7720
I am running on SQL Server 2017 Express. I have tried running a chained CASE-WHEN statement i.e.
CASE WHEN
THEN
WHEN
THEN
and so on...
but I can figure out how to add the logic of subtracting the remaining amount. Please find my code below.
SELECT 'emp_name' AS 'Director',
SUM(ABS([Transaction Amount])) AS 'INCOME',
CASE WHEN (SUM(ABS([Transaction Amount])) < 14000)
THEN ((SUM(ABS([Transaction Amount])) - 14000) * 0.1050)
WHEN (SUM(ABS([Transaction Amount])) > 14000 and (SUM(ABS([Transaction Amount])) < 48001))
THEN (((SUM(ABS([Transaction Amount])) - 14000) * 0.1050) - 48000) * 0.1750 end AS 'Income Tax'
FROM Transactions
EDIT 1:
Input Data:
Transaction Type| PAYEE | Transaction Amount
DEBIT | DEBIT | -184.00
CREDIT | CREDIT | 4000.00
...
Output Data:
Director | INCOME | Income Tax
emp_name | 45100.00| NULL
Please let me know where I am going wrong or if my thinking is incorrect.

A correlated subquery may be the simplest to read and understand:
declare #t table (MinLimitExclusive int, MaxLimitInclusive int, TaxRate decimal(5,2))
insert into #t(MinLimitExclusive,MaxLimitInclusive,TaxRate) values
(0 ,14000 , 10.50),
(14000,48000 , 17.50),
(48000,70000 , 30.00),
(70000,1000000, 33.00)
declare #transactions table (Income decimal(10,2))
insert into #transactions (Income) values (49000)
select
(Income - MinLimitExclusive) * TaxRate / 100 +
(select SUM((rates2.MaxLimitInclusive - rates2.MinLimitExclusive) * rates2.TaxRate / 100)
from #t rates2 where rates2.MaxLimitInclusive <= rates.MinLimitExclusive)
from
#transactions tr
inner join
#t rates
on
tr.Income > rates.MinLimitExclusive and tr.Income <= rates.MaxLimitInclusive
It's remarkably simplified when you realise that the only maths you need to do related to the actual income is related to the bracket it actually fits in - all of the lower rate brackets, by implication, were used in their entirety so you can compute those other taxes purely from the rates table.
I've changed your rates data slightly to make the computations straightforward and not need lots of +/-1 adjustments.

I suggest that you start with a MinLimit of 1 instead of 0. The rest of the calculation is straight forward:
declare #taxslabs table (minlimit int, maxlimit int, taxrate decimal(18, 2));
insert into #taxslabs values
(1, 14000, 10.50),
(14001, 48000, 17.50),
(48001, 70000, 30.00),
(70001, 1000000, 33.00);
select persons.*, taxslabs.*, taxableamount, taxableamount * taxrate / 100 as taxamount
from (values
(1, 49000),
(2, 70000),
(3, 70001)
) as persons(id, income)
cross join #taxslabs as taxslabs
cross apply (select case when income <= maxlimit then income else maxlimit end - minlimit + 1) as ca(taxableamount)
where minlimit <= income
You can place this query inside a subquery and use GROUP BY ... SUM() or SUM() OVER (PARTITION BY) to calculate the sum of taxes.
Sample output:
| id | income | minlimit | maxlimit | taxrate | taxableamount | taxamount |
|----|--------|----------|----------|---------|---------------|------------------|
| 1 | 49000 | 1 | 14000 | 10.50 | 14000 | 1470.000000 |
| 1 | 49000 | 14001 | 48000 | 17.50 | 34000 | 5950.000000 |
| 1 | 49000 | 48001 | 70000 | 30.00 | 1000 | 300.000000 |
| 2 | 70000 | 1 | 14000 | 10.50 | 14000 | 1470.000000 |
| 2 | 70000 | 14001 | 48000 | 17.50 | 34000 | 5950.000000 |
| 2 | 70000 | 48001 | 70000 | 30.00 | 22000 | 6600.000000 |
| 3 | 70001 | 1 | 14000 | 10.50 | 14000 | 1470.000000 |
| 3 | 70001 | 14001 | 48000 | 17.50 | 34000 | 5950.000000 |
| 3 | 70001 | 48001 | 70000 | 30.00 | 22000 | 6600.000000 |
| 3 | 70001 | 70001 | 1000000 | 33.00 | 1 | 0.330000 |

i think this relevant query using group by on transaction table and join to rate taxe table can be down excepted result :
CREATE TABLE #Transaction
(
tID int PRIMARY KEY,
tIdUser varchar(50),
Amount decimal(9,3)
);
CREATE TABLE #RefTaxe
(
pID int PRIMARY KEY,
minLimit int,
maxLImit int,
rate decimal(9,3)
);
INSERT INTO #Transaction
SELECT 1, 'User1', 1259.3
UNION
SELECT 2, 'User1', 10259.3
UNION
SELECT 3, 'User3', 30581.3
UNION
SELECT 4, 'User2', 75000.36
UNION
SELECT 5, 'User2', 15000.36
UNION
SELECT 6, 'User4', 45000.36
UNION
SELECT 7, 'User4', 5000.36
INSERT INTO #RefTaxe
select 1,0,14000,10.50
UNION
SELECT 2,14001,48000,17.50
UNION
SELECT 3,48001,70000,30.00
UNION
SELECT 4,70001,1000000,33.00
-- SELECT * FROM #Transaction
-- SELECT * FROM #RefTaxe
-- SELECT tIdUser,SUM(AMOUNT) as SumAmount, CAST(FLOOR(SUM(AMOUNT))as int) as SumAsInt FROM #Transaction GROUP BY tIdUser
/***/
-- Perform select
/***/
SELECT tIdUser, SumAmount as 'DetaxedAmount' ,SumAmount * (rate/100) as TaxOfAmount, SumAmount+ SumAmount * (rate/100) as TaxedAmount
FROM #RefTaxe RT
JOIN (
SELECT tIdUser,SUM(AMOUNT) as SumAmount, CAST(FLOOR(SUM(AMOUNT))as int) as SumAsInt
FROM #Transaction GROUP BY tIdUser
) AS GroupedTR ON RT.minLimit <= SumAsInt AND RT.maxLImit >= SumAsInt
/***/
DROP TABLE #Transaction
DROP TABLE #RefTaxe
Result output :
tIdUser DetaxedAmount TaxOfAmount TaxedAmount
User1 11518.600 1209.453000 12728.053
User2 90000.720 29700.237600 119700.958
User3 30581.300 5351.727500 35933.028
User4 50000.720 15000.216000 65000.936

This Query is calculating very exact results against tax rules define by Pakistan Government.[Thanks & Regards - Noman Ali].
Select
Salary,
Salary*12 as YearlySalary,
case
when salary * 12 Between 600001 and 1200000 then ((salary * 12 - 600000) / 100 * 2.5) / 12
when salary * 12 Between 1200001 and 2400000 then (15000 + (salary * 12 - 1200000) / 100 * 12.50) / 12
when salary * 12 Between 2400001 and 3600000 then (165000 + (salary * 12 - 2400000) / 100 * 20.0) / 12
when salary * 12 Between 3600001 and 6000000 then (405000 + (salary * 12 - 3600000) / 100 * 25.0 ) / 12
when salary * 12 Between 6000001 and 12000000 then (1005000 + (salary* 12 - 6000000) / 100 * 32.5 ) / 12
when salary * 12 > 12000001 then (2955000 + (salary * 12 - 12000000) / 100 * 35.0 ) / 12
else 0 end as IncomeTax
from Employees

Related

How to use Lag with while to calculate the effectiveness of discount , SQL

I want to calculate the effectiveness of a discount.
I want to update the last column as 'pozitive' if s_quantity increased and negative, if decreased. Neutral, if no change. I've written the code:
DECLARE #count AS INT
SET #count=1
WHILE #count< 316
BEGIN
IF product_id = #count
WHEN s_quantity > (LAG(s_quantity) OVER (ORDER BY product_id ASC, discount ASC))
UPDATE [SampleRetail].[sale].[Analiz] SET hesap_kitap = 'pozitif'
SET #count +=1
IF #count > 316
BREAK
ELSE
CONTINUE
END
Where do I make the mistake? Can you help me?
We can do this with the function LAG() in a SELECT.
(see the dbFiddle link below for the test schema.)
SELECT
period,
sales,
LAG(sales) OVER (ORDER BY period) p_1,
sales - LAG(sales) OVER (ORDER BY period) increase
FROM sales
ORDER BY period;
period | sales | p_1 | increase
-----: | ----: | ---: | -------:
1 | 100 | null | null
2 | 200 | 100 | 100
3 | 300 | 200 | 100
4 | 250 | 300 | -50
5 | 500 | 250 | 250
6 | 500 | 500 | 0
WITH sale as (
SELECT
period,
sales,
LAG(sales) OVER (ORDER BY period) p_1
FROM sales)
SELECT
period,
sales,
sales - p_1 increase,
CASE WHEN sales < p_1 THEN 'negative'
WHEN sales = p_1 THEN 'stable'
ELSE 'positive' END AS "change"
FROM sale;
period | sales | increase | change
-----: | ----: | -------: | :-------
1 | 100 | null | positive
2 | 200 | 100 | positive
3 | 300 | 100 | positive
4 | 250 | -50 | negative
5 | 500 | 250 | positive
6 | 500 | 0 | stable
db<>fiddle here

SQL Server Cumulative Sum breaks

I have one Amount field and I want to build the following table (CumulativeSum and GroupNo columns):
| Id | Amount | CumulativeSum | GroupNo |
| 1 | 1000 | 1000 | 0 |
| 2 | 2000 | 3000 | 0 |
| 3 | 1000 | 4000 | 0 |
| 4 | 3000 | 3000 | 1 |
| 5 | 2000 | 5000 | 1 |
| 6 | 3000 | 3000 | 2 |
| 7 | 1000 | 4000 | 2 |
| 8 | 4000 | 4000 | 3 |
| 9 | 2000 | 2000 | 4 |
Note: when ever the CumulativeSum becomes grater than 5000, I need it to start summing from 0 with a new Group Number (GroupNo).
For Example the sum of rows 1, 2 and 3 equals 4000. The amount of row 4 is 3000 and 4000 + 3000 = 7000 and it is grater than 5000 so it must break and start from 0 again.
The easiest way to do this is with a recursive CTE. These are effectively loops, where the next value is calculated based on previous values. Here's a link to a basic example and writeup of how they work: https://www.sqlservertutorial.net/sql-server-basics/sql-server-recursive-cte/
In this case, it checks if the previous value + the current value is over 5000; if so, it resets the counter and adds 1 to the GroupNo; otherwise it just adds the current value to CumulativeSum.
CREATE TABLE #Amts (ID int PRIMARY KEY, Amount int);
INSERT INTO #Amts (ID, Amount)
VALUES
(1, 1000), -- | 1000 | 0 |
(2, 2000), -- | 3000 | 0 |
(3, 1000), -- | 4000 | 0 |
(4, 3000), -- | 3000 | 1 |
(5, 2000), -- | 5000 | 1 |
(6, 3000), -- | 3000 | 2 |
(7, 1000), -- | 4000 | 2 |
(8, 4000), -- | 4000 | 3 |
(9, 2000); -- | 2000 | 4 |
WITH RunningTotals AS
(SELECT A.ID, A.Amount, A.Amount AS CumulativeSum, 0 AS GroupNo
FROM #Amts A
WHERE ID = 1
UNION ALL
SELECT RT.ID + 1,
A.Amount,
CASE WHEN RT.CumulativeSum + A.Amount > 5000 THEN A.Amount
ELSE RT.CumulativeSum + A.Amount END,
CASE WHEN RT.CumulativeSum + A.Amount > 5000 THEN RT.GroupNo + 1
ELSE RT.GroupNo END
FROM RunningTotals RT
INNER JOIN #Amts A ON RT.ID + 1 = A.ID
)
SELECT *
FROM RunningTotals
OPTION (MAXRECURSION 1000);
Results are as follows
ID Amount CumulativeSum GroupNo
1 1000 1000 0
2 2000 3000 0
3 1000 4000 0
4 3000 3000 1
5 2000 5000 1
6 3000 3000 2
7 1000 4000 2
8 4000 4000 3
9 2000 2000 4
Notes
This currently requires the IDs to be consecutive and starting with 1. If not, you may need to make an initial CTE with ROW_NUMBER() OVER (ORDER BY Id) to get this
I've put MAXRECURSION 1000 on there - that means it will do this looping 1000 times. If you have a larger table than this, you'll need to increase that number.
Update: fixed bug in code ( RT.GroupNo + 1 was in wrong spot). Also added 'Amount' column to output.

Grouping by a same range of multiple values with sum and counts in SQL

I want to group different colons in same range by row. Example:
Amount1 | Amount2
------------------------
20,00 | 30,00
35,00 | 32,00
12,00 | 51,00
101,00 | 100,00
Here result should be;
Range |TotalAmount1 |TotalAmount2 | CountAmount1 | CountAmount2 | RateOfCountAmount1
-----------------------------------------------------------------------------
0-50 | 67,00 | 62,00 | 3 | 1 | %75
50-100 | 0,00 | 151,00 | 0 | 2 | %0
100+ | 101,00 | 0,00 | 1 | 0 | %25
Total | 168,00 | 213,00 | 4 | 3 | %100
Here is the example : http://sqlfiddle.com/#!9/05fd3
you can query like this
;with cte as (
select case when amount1 < 50 then '0-50'
when amount1 between 50.01 and 100 then '50-100'
when amount1 > 100 then '100+' end as rngamt1,
case when amount2 < 50 then '0-50'
when amount2 between 5.01 and 100 then '50-100'
when amount2 > 100 then '100+' end as rngamt2,
* from amounts
), cte2 as (select coalesce(rngamt1, rngamt2) as [Range], isnull(a.TotalAmount1,0) as TotalAmount1, isnull(b.TotalAmount2, 0) as TotalAmount2, isnull( a.TotalCount1 , 0) as TotalCount1, isnull(b.TotalCount2, 0) as Totalcount2 from
(select rngamt1, sum(amount1) TotalAmount1, count(amount1) TotalCount1 from cte c
group by rngamt1) a
full join
(select rngamt2, sum(amount2) TotalAmount2, count(amount2) TotalCount2 from cte c
group by rngamt2) b
on a.rngamt1 = b.rngamt2
)
select *, (TotalCount1 * 100 )/sum(TotalCount1) over () as RateCount1
from cte2
union
select 'Total' as [Range], sum(TotalAmount1) as TotalAmount1, sum(totalAmount2) as TotalAmount2,
sum(TotalCount1) as TotalCount1, sum(Totalcount2) as TotalCount2, (sum(TotalCount1)*100)/Sum(TotalCount1) as RateCount1 from cte2

The highest value from list-distinct

Can anyone help me with query, I have table
vendorid, agreementid, sales
12001 1004 700
5291 1004 20576
7596 1004 1908
45 103 345
41 103 9087
what is the goal ?
when agreemtneid >1 then show me data when sales is the highest
vendorid agreementid sales
5291 1004 20576
41 103 9087
Any ideas ?
Thx
Well you could try using a CTE and ROW_NUMBER something like
;WITH Vals AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY AgreementID ORDER BY Sales DESC) RowID
FROM MyTable
WHERE AgreementID > 1
)
SELECT *
FROM Vals
WHERE RowID = 1
This will avoid you returning multiple records with the same sale.
If that was OK you could try something like
SELECT *
FROM MyTable mt INNER JOIN
(
SELECT AgreementID, MAX(Sales) MaxSales
FROM MyTable
WHERE AgreementID > 1
) MaxVals ON mt.AgreementID = MaxVals.AgreementID AND mt.Sales = MaxVals.MaxSales
SELECT TOP 1 WITH TIES *
FROM MyTable
ORDER BY DENSE_RANK() OVER(PARTITION BY agreementid ORDER BY SIGN (SIGN (agreementid - 2) + 1) * sales DESC)
Explanation
We break table MyTable into partitions by agreementid.
For each partition we construct a ranking or its rows.
If agreementid is greater than 1 ranking will be equal to ORDER BY sales DESC.
Otherwise ranking for every single row in partition will be the same: ORDER BY 0 DESC.
See how it looks like:
SELECT *
, SIGN (SIGN (agreementid - 2) + 1) * sales AS x
, DENSE_RANK() OVER(PARTITION BY agreementid ORDER BY SIGN (SIGN (agreementid - 2) + 1) * sales DESC) AS rnk
FROM MyTable
+----------+-------------+-------+-------+-----+
| vendorid | agreementid | sales | x | rnk |
+----------|-------------|-------+-------+-----+
| 0 | 0 | 3 | 0 | 1 |
| -1 | 0 | 7 | 0 | 1 |
| 0 | 1 | 3 | 0 | 1 |
| -1 | 1 | 7 | 0 | 1 |
| 41 | 103 | 9087 | 9087 | 1 |
| 45 | 103 | 345 | 345 | 2 |
| 5291 | 1004 | 20576 | 20576 | 1 |
| 7596 | 1004 | 1908 | 1908 | 2 |
| 12001 | 1004 | 700 | 700 | 3 |
+----------+-------------+-------+-------+-----+
Then using TOP 1 WITH TIES construction we leave only rows where rnk equals 1.
you can try like this.
SELECT TOP 1 sales FROM MyTable WHERE agreemtneid > 1 ORDER BY sales DESC
I really do not know the business logic behind agreement_id > 1. It looks to me you want the max sales (with ties) by agreement id regardless of vendor_id.
First, lets create a simple sample database.
-- Sample table
create table #sales
(
vendor_id int,
agreement_id int,
sales_amt money
);
-- Sample data
insert into #sales values
(12001, 1004, 700),
(5291, 1004, 20576),
(7596, 1004, 1908),
(45, 103, 345),
(41, 103, 9087);
Second, let's solve this problem using a common table expression to get a result set that has each row paired with the max sales by agreement id.
The select statement just applies the business logic to filter the data to get your answer.
-- CTE = max sales for each agreement id
;
with cte_sales as
(
select
vendor_id,
agreement_id,
sales_amt,
max(sales_amt) OVER(PARTITION BY agreement_id) AS max_sales
from
#sales
)
-- Filter by your business logic
select * from cte_sales where sales_amt = max_sales and agreement_id > 1;
The screen shot below shows the exact result you wanted.

How to calculate SUM balance of all accounts in T-SQL?

I have this table and data
CREATE TABLE #transactions (
[transactionId] [int] NOT NULL,
[accountId] [int] NOT NULL,
[dt] [datetime] NOT NULL,
[balance] [smallmoney] NOT NULL,
CONSTRAINT [PK_transactions_1] PRIMARY KEY CLUSTERED
( [transactionId] ASC)
)
INSERT #transactions ([transactionId], [accountId], [dt], [balance]) VALUES
(1, 1, CAST(0x0000A13900107AC0 AS DateTime), 123.0000),
(2, 1, CAST(0x0000A13900107AC0 AS DateTime), 192.0000),
(3, 1, CAST(0x0000A13A00107AC0 AS DateTime), 178.0000),
(4, 2, CAST(0x0000A13B00107AC0 AS DateTime), 78.0000),
(5, 2, CAST(0x0000A13D011D1860 AS DateTime), 99.0000),
(6, 2, CAST(0x0000A13F00000000 AS DateTime), 97.0000),
(7, 1, CAST(0x0000A13D0141E640 AS DateTime), 201.0000),
(8, 3, CAST(0x0000A1420094DD60 AS DateTime), 4000.0000),
(9, 3, CAST(0x0000A14300956A00 AS DateTime), 4100.0000),
(10, 3, CAST(0x0000A14700000000 AS DateTime), 4200.0000),
(11, 2, CAST(0x0000A14B00B84BB0 AS DateTime), 110.0000)
I need two queries.
For each transaction, I want to return in a query the most recent balance for each account, and an extra column with a SUM of each account balance at that point in time.
Same as 1 but grouped by date without the time portion. So the latest account balance at the end of each day (where there is a transaction in any account) for each account, but SUMed together as in 1.
Data above is sample data that I just made up, but my real table has hundreds of rows and ten accounts (which may increase soon). Each account has a unique accountId. Seems quite a tricky piece of SQL.
EXAMPLE
For 1. I need a result like this:
+---------------+-----------+-------------------------+---------+-------------+
| transactionId | accountId | dt | balance | sumBalances |
+---------------+-----------+-------------------------+---------+-------------+
| 1 | 1 | 2013-01-01 01:00:00.000 | 123 | 123 |
| 2 | 1 | 2013-01-01 01:00:00.000 | 192 | 192 |
| 3 | 1 | 2013-01-02 01:00:00.000 | 178 | 178 |
| 4 | 2 | 2013-01-03 01:00:00.000 | 78 | 256 |
| 5 | 2 | 2013-01-05 17:18:00.000 | 99 | 277 |
| 7 | 1 | 2013-01-05 19:32:00.000 | 201 | 300 |
| 6 | 2 | 2013-01-07 00:00:00.000 | 97 | 298 |
| 8 | 3 | 2013-01-10 09:02:00.000 | 4000 | 4298 |
| 9 | 3 | 2013-01-11 09:04:00.000 | 4100 | 4398 |
| 10 | 3 | 2013-01-15 00:00:00.000 | 4200 | 4498 |
| 11 | 2 | 2013-01-19 11:11:00.000 | 110 | 4511 |
+---------------+-----------+-------------------------+---------+-------------+
So, for transactionId 8, I take the latest balance for each account in turn and then sum them. AccountID 1: is 201, AccountId 2 is 97 and AccountId 3 is 4000. Therefore the result for transactionId 8 will be 201+97+4000 = 4298. When calculating the set must be ordered by dt
For 2. I need this
+------------+-------------+
| date | sumBalances |
+------------+-------------+
| 01/01/2013 | 192 |
| 02/01/2013 | 178 |
| 03/01/2013 | 256 |
| 05/01/2013 | 300 |
| 07/01/2013 | 298 |
| 10/01/2013 | 4298 |
| 11/01/2013 | 4398 |
| 15/01/2013 | 4498 |
| 19/01/2013 | 4511 |
+------------+-------------+
So on date 15/01/2013 the latest account balance for each account in turn (1,2,3) is 201,97,4200. So the result for that date would be 201+97+4200 = 4498
This gives your first desired resultset (SQL Fiddle)
WITH T
AS (SELECT *,
balance -
isnull(lag(balance) OVER (PARTITION BY accountId
ORDER BY dt, transactionId), 0) AS B
FROM #transactions)
SELECT transactionId,
accountId,
dt,
balance,
SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM T
ORDER BY dt;
It subtracts the current balance of the account from the previous balance to get the net difference then calculates a running total of those differences.
And that can be used as a base for your second result
WITH T1
AS (SELECT *,
balance -
isnull(lag(balance) OVER (PARTITION BY accountId
ORDER BY dt, transactionId), 0) AS B
FROM #transactions),
T2 AS (
SELECT transactionId,
accountId,
dt,
balance,
ROW_NUMBER() OVER (PARTITION BY CAST(dt AS DATE) ORDER BY dt DESC, transactionId DESC) AS RN,
SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM T1)
SELECT CAST(dt AS DATE) AS [date], sumBalances
FROM T2
WHERE RN=1
ORDER BY [date];
Part 1
; WITH a AS (
SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
FROM #transactions t
)
, b AS (
SELECT t.*
, transamount = t.balance - ISNULL(t0.balance,0)
FROM a t
LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY dt, transactionId)
FROM b
ORDER BY dt
Part 2
; WITH a AS (
SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
FROM #transactions t
)
, b AS (
SELECT t.*
, transamount = t.balance - ISNULL(t0.balance,0)
FROM a t
LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
, c AS (
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY CAST(dt AS DATE))
, r1 = ROW_NUMBER()OVER(PARTITION BY accountId, CAST(dt AS DATE) ORDER BY dt DESC)
FROM b
)
SELECT dt = CAST(dt AS DATE)
, sumBalance
FROM c
WHERE r1 = 1
ORDER BY CAST(dt AS DATE)

Resources