Sum Running value with debit and credit based on cndition - sql

I have this query to running value credit with running value
USE tempdb
GO
DROP TABLE TestTable
CREATE TABLE TestTable (ID INT, CREDIT INT,DEBIT INT, TXT NVARCHAR(MAX))
INSERT INTO TestTable (ID, CREDIT,DEBIT,TXT)
SELECT 1, 10,-20,'A' UNION ALL
SELECT 2, 20,-30,'B' UNION ALL
SELECT 3, 30,0,'C' UNION ALL
SELECT 4, 40,0,'C' UNION ALL
SELECT 5, 50,-30,'B' UNION ALL
SELECT 6, 60,0,'A' UNION ALL
SELECT 7, 70,0,'A'
GO
SELECT ID,txt, CREDIT,DEBIT
,SUM(CREDIT) OVER(ORDER BY ID ROWS UNBOUNDED PRECEDING) AS RunningTotal
FROM TestTable AS T
i need this query to sum based on conditions with debit or credit
if txt = 'A' then Running value + credit
if txt = 'B' then Running value + debit
thanks

This works on Oracle :
SELECT SUM(CASE WHEN txt = 'A' THEN CREDIT WHEN txt = 'B' THEN DEBIT END)
FROM TestTable

You might just want a conditional in the sum():
SELECT ID, txt, CREDIT, DEBIT,
SUM(CASE WHEN txt = 'A' THEN CREDIT
WHEN txt = 'B' THEN DEBIT
ELSE 0
END) OVER (ORDER BY ID) AS RunningTotal
FROM TestTable T;
The windows clause is (unbounded preceding) is unnecessary for the cumulative sum. The else 0 is to handle the initial case if txt is neither A nor B. I assume you would want 0.

Related

I need a query that can calculate a value based on conditions

I have data such as:
Type
Amount
a
1000
a
5000
b
4000
b
2000
c
300
And would like to sum the amounts where Type is a and b, and minus the amounts where type is c.
I only know how to sum based on one condition, ie:
select sum(amount)
from xxxx
where type = 'a'
Do I need to do a sub-select or is there an easier way?
You can use a case statement inside sum:
select sum(case when type in ('a', 'b') then amount when type = 'c' then -amount end)
from table_name;
Use GROUP BY:
SELECT
Type,
sum(Amount)
FROM table
GROUP BY Type
https://www.postgresql.org/docs/10/queries-table-expressions.html#QUERIES-GROUP
https://www.postgresql.org/docs/10/tutorial-agg.html
WITH CTE(Type , Amount) AS
(
SELECT 'a' ,1000 UNION ALL
SELECT 'a' , 5000 UNION ALL
SELECT 'b' , 4000 UNION ALL
SELECT 'b' , 2000 UNION ALL
SELECT 'c' , 300
)
SELECT
SUM(CASE WHEN C.TYPE IN ('a','b')THEN C.Amount
ELSE 0
END) -
SUM(CASE WHEN C.TYPE='c' THEN C.Amount
ELSE 0
END)
FROM CTE AS C
for mariaDB
SELECT
debit - credit
FROM
(SELECT sum(Amount) AS debit FROM Your_table WHERE Type IN ('a', 'b')) AS condition1,
(SELECT sum(Amount) AS credit FROM Your_table WHERE Type NOT IN ( 'a', 'b' )) AS condition2;

I want to get difference of sum of a column from two tables

I have 2 tables: transactions and transactions_archive. Each of them has fields accountno,drcr(which has values either as C or D) and field amount. I want to get difference of sum of all 'C' in both transactions and transactions_archive and sum of all 'D' in both transactions and transactions_archive.
What query can I use to get this answer.
I tried this unsuccessfully:
select (
select accountno,drcr,sum(amount)as total from
(
select accountno,drcr,amount
from ebank.tbtransactions
where drcr='C'
union all
select accountno,drcr,amount
from ebank.tbtransactions_archive
where drcr='C'
)
)
-
(select accountno,drcr,sum(amount)as total
from (
select accountno,drcr,amount
from ebank.tbtransactions
where drcr='D'
union all
select accountno,drcr,amount
from ebank.tbtransactions_archive
where drcr='D'
)
)
group by accountno,drcr;
If I understand correctly, you want to subtract all the "D"s from the "C"s. Combine the tables using UNION ALL and use conditional aggregation:
select accountno,
sum(case when drcr = 'C' then amount else - amount end)as total
from ((select accountno, drcr, amount
from ebank.tbtransactions
) union all
(select accountno, drcr, amount
from ebank.tbtransactions_archive
)
) t
where drcr in ('D', 'C')
group by accountno;
SELECT top 1 (amount - nextamount) as diff from(
SELECT
amount,LEAD(amount, 1,0) OVER (ORDER BY YEAR(drcr)) AS nextamount FROM(
SELECT drcr, sum(amount) as amount from transactions JOIN transactions_archive on transactions.drcr and transactions_archive.drcr GROUP BY drcr))

SQL - split values into buckets using reference table

I've tried to describe the challenge below; but perhaps the best way to understand might be to run the sample SQL and work backwards from the sample output table (#IncomingSplitBucket)
Im certain there is an eloquent way to code this – but its beyond any of my best efforts.
The challenge is titled:
Splitting Numbers into Buckets
Each Customer has Payment Buckets (#bucket). I’m looking to Assign payments into Buckets as the payments are made see table (#incoming).
Payments can span buckets and can be for +/- amounts.
Using the (#incoming) and (#buckets) information – the AIM is to assign the payments into buckets. Payments should be split when they span a bucket amount.
The table #IncomingSplitBucket – provide the desired OUTPUT. One way to understand the requirements is to perhaps look at this and work backwards.
I have tried and failed many approaches to this problem.
/*
Please run code and review
*/
--===========================================
--t1 - PAYMENT SCHEDULE SPLIT INTO BUCKET
--===========================================
DECLARE #bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)
INSERT INTO #bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union
SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union
SELECT 3,'a', '5000' union
SELECT 3,'b', '5000'
--===========================================
--t2 - PAYMENTS COMING IN
--===========================================
DECLARE #incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)
INSERT INTO #incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union
SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '1000'
--=================================================================
--t3 - THIS IS WHAT THE OUTPUT DATA SHOULD LOOK LIKE
--================================================================
DECLARE #IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)
INSERT INTO #IncomingSplitBucket
SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union
SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','1000'
--=================================================================
--Outputs and Data Checks
--================================================================
--REVIEW DATA
select * from #bucket
select * from #incoming
select * from #IncomingSplitBucket --(sample output)
--DATA Check - The SUM AmountBucket of Grouped BucketSeqID = the #bucket amounts see table
SELECT CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2
--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID = the #incoming amounts see table
SELECT CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2
Updated complexity request: (10/12/2019)
When negative amounts are received that take money out of the
buckets.
When the amount received is greater than buckets – an ‘overflow
bucket’ is used (called ‘x’ in the Expect Output)
Thanks
--===========================================
--t1 - BUCKETS
--===========================================
DECLARE #bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)
INSERT INTO #bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union
SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union
SELECT 3,'a', '5000' union
SELECT 3,'b', '5000'
--===========================================
--t2 - PAYMENTS
--===========================================
DECLARE #incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)
INSERT INTO #incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union
SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 2,5, '-3000' union
SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '500' union
SELECT 3,5, '200' union
SELECT 3,6, '-500' union
SELECT 3,7, '800' union
SELECT 3,8, '-400' union
SELECT 3,9, '500'
--=================================================================
--t3 - EXPECTED OUTPUT
--================================================================
DECLARE #IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)
INSERT INTO #IncomingSplitBucket
SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union
SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 2,5,'d','-1000' union
SELECT 2,5,'c','-1000' union
SELECT 2,5,'b','-1000' union
SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','200' union
SELECT 3,5,'b','-500' union
SELECT 3,6,'b','800' union
SELECT 3,7,'b','-400' union
SELECT 3,8,'b','400' union
SELECT 3,8,'x','100'
--=================================================================
--Outputs and Data Checks
--================================================================
--REVIEW DATA
select * from #bucket
select * from #incoming
select * from #IncomingSplitBucket --(expected output)
--DATA Check - The SUM AmountBucket of Grouped BucketSeqID = the #bucket amounts see table
SELECT CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2
--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID = the #incoming amounts see table
SELECT CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2
First I will use a common table expression (cte) to change the column names so I dont have similar ones between tables to make our life easy, plus will convert the bucket names a,b,c,d to a seq of 1,2,3,4 for simplicity.
Then I will continue with another recursive cte to take the first bucket and the first incoming payment, if the bucket is not filled for the next record I will use the same unfilled bucket else i will use the next bucket, same thing with the incoming payment if the incoming payment fits in the remaining part of the bucket on next row i will go to the next payment if not I will use the rest of the incoming payment till all incoming payment are finished.
Please see the below CTE
;with bucket as (
select CustID BucketCustID,BucketSeqID,
case BucketSeqID when 'a' then 1 when 'b' then 2 when 'c' then 3 when 'd' then 4 end BucketSeq
,Amount bucketAmount from #bucket
),incoming as (
select CustID IncomingCustID, IncomingSeqID ,Amount [IncomingAmount] from #incoming
),result as (
select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
from bucket b
inner join incoming i on i.IncomingCustID=b.BucketCustID and i.IncomingSeqID=1
where b.BucketSeq=1
union all
select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
from (
select
b.BucketCustID,i.IncomingSeqID,b.BucketSeqID,b.BucketSeq
,case when r.BucketSeq=b.BucketSeq then r.bucketAmount else b.bucketAmount end bucketAmount
,case when r.IncomingSeqID=i.IncomingSeqID then r.IncomingAmount else i.IncomingAmount end IncomingAmount
from result r
inner join bucket b on b.BucketCustID=r.BucketCustID and b.BucketSeq=r.BucketSeq+(case when r.bucketAmount=0 then 1 else 0 end)
inner join incoming i on i.IncomingCustID=r.BucketCustID and i.IncomingSeqID=r.IncomingSeqID+(case when r.IncomingAmount=0 then 1 else 0 end)
) Prev
)
select BucketCustID CustID,IncomingSeqID,BucketSeqID,InBucket AmountBucket
from result r
order by BucketCustID,IncomingSeqID,BucketSeqID
The output matches your desired output as below:-
CustID IncomingSeqID BucketSeqID AmountBucket
1 1 a 1000.00
1 2 b 1000.00
1 2 c 1000.00
1 3 c 1000.00
1 3 d 2000.00
2 1 a 5000.00
2 2 b 3000.00
2 3 b 2000.00
2 4 c 1000.00
2 4 d 1000.00
3 1 a 3000.00
3 2 a 2000.00
3 2 b 1000.00
3 3 b 3000.00
3 4 b 1000.00

how to find all column records are same or not in group by column in SQL

How to find all column values are same in Group by of rows in table
CREATE TABLE #Temp (ID int,Value char(1))
insert into #Temp (ID ,Value ) ( Select 1 ,'A' union all Select 1 ,'W' union all Select 1 ,'I' union all Select 2 ,'I' union all Select 2 ,'I' union all Select 3 ,'A' union all Select 3 ,'B' union all Select 3 ,'1' )
select * from #Temp
Sample Table:
How to find all column value of 'Value' column are same or not if group by 'ID' Column.
Ex: select ID from #Temp group by ID
For ID 1 - Value column records are A, W, I - Not Same
For ID 2 - Value column records are I, I - Same
For ID 3 - Value column records are A, B, 1 - Not Same
I want the query to get a result like below
When all items in the group are the same, COUNT(DISTINCT Value) would be 1:
SELECT Id
, CASE WHEN COUNT(DISTINCT Value)=1 THEN 'Same' ELSE 'Not Same' END AS Result
FROM MyTable
GROUP BY Id
If you're using T-SQL, perhaps this will work for you:
SELECT t.ID,
CASE WHEN MAX(t.RN) > 1 THEN 'Same' ELSE 'Not Same' END AS GroupResults
FROM(
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID, VALUE ORDER BY ID) RN
FROM #Temp
) t
GROUP BY t.ID
Usally that's rather easy: Aggregate per ID and count distinct values or compare minimum and maximum value.
However, neither COUNT(DISTINCT value) nor MIN(value) nor MAX(value) take nulls into consideration. So for an ID having value 'A' and null, these would detect uniqueness. Maybe this is what you want or nulls don't even occur in your data.
But if you want nulls to count as a value, then select distinct values first (where null gets a row too) and count then:
select id, case when count(*) = 1 then 'same' else 'not same' end as result
from (select distinct id, value from #temp) dist
group by id
order by id;
Rextester demo: http://rextester.com/KCZD88697

Case statement with Ranking function

Hello looking for help with ranking.
I'm working with Teradata using SQL and I'm trying to rank a list by a specific group and then by age.
For example: I want to rank by group then only rank those under the selected group that are under 21 years old.
However, when I use the query below it seems to not take into account the members in a group and assigns only if they meet the criteria in the case statement.
select
policy,
age,
case when age <'21' then '1'else '0' end as Under21,
case when age <'21' then dense_rank () over (order by group, age desc) else '0' end as Rank_Under_21
from Table
You can use the partition by clause:
dense_rank () over (partition by policy, case when age < 21 then 1 end
order by group, age desc)
NB: If age is a numerical field (it should be), then don't compare it with a string: leave out the quotes. If age is of a string type, then be aware that the comparison with another string will be alphabetical, and thus '9' > '21'.
Your code still ranks all ages, the CASE simply replaces the high age ranks with zero.
Another solution (besides to #trincot's answer) which moves the CASE into the RANK:
CASE
WHEN age < 21
THEN Rank ()
Over (PARTITION BY policy
ORDER BY CASE WHEN age < 21 THEN age END DESC)
ELSE 0
END
This also ranks all ages, but the high ones are sorted last und thus got a high rank, which is replaced by the outer CASE with zero.
The below code is for MS SQL Server. Thanks to #Victor Hugo Terceros, for sample code.
DECLARE #tbl TABLE
(
age INT,
grp VARCHAR(20)
)
INSERT INTO #tbl
SELECT 1,
'A'
UNION
SELECT 12,
'A'
UNION
SELECT 20,
'A'
UNION
SELECT 19,
'B'
UNION
SELECT 30,
'B'
UNION
SELECT 11,
'B'
UNION
SELECT 4,
'C'
UNION
SELECT 14,
'C'
UNION
SELECT 5,
'B'
UNION
SELECT 16,
'D'
SELECT grp AS Policy,
age,
under21 AS Under21,
CASE
WHEN under21 = 0 THEN Dense_rank()
OVER(
partition BY grp
ORDER BY under21age DESC)
ELSE 0
END AS Rank_Under_21
FROM (SELECT CASE
WHEN age < 21 THEN 0
ELSE 1
END AS Under21,
CASE
WHEN age < 21 THEN age
ELSE 0
END AS under21age,
age,
grp
FROM #tbl) AS t
What I suggest is a Partition by your Group column and then Rank by Age
---------------Test Table with data---------
declare #tbl table(age int, policy varchar(20))
insert into #tbl
select 1, 'A' union
select 12, 'A' union
select 20, 'A' union
select 19, 'B' union
select 30, 'B' union
select 11, 'B' union
select 4, 'C' union
select 14, 'C' union
select 5, 'B' union
select 16, 'D'
---------------Main Query--------------------
select policy,age,
'1' as Under21,
rank() over (partition by policy order by age desc) as Rank_Under_21
from #tbl
where age <21
union
select policy,age,
'0' as Under21,
0 as Rank_Under_21
from #tbl
where age >=21
order by policy asc,age desc