Sum the balance in history table - sql-server-2008

I need help I'm not able to achieve desired output. I have used cursor as shown below. I have two tables 1. Transaction 2. History. I need to sum the amount in Transaction table for ID and insert it in history table under revised_amount column and also store the amount before sum in Original_amount column. Below is the example. Thanks
Tran_ID ID Amount
1066 Dhan Group $4,800.00
1327 Dhan Group $2,670.00
1329 Dhan Group $1,800.00
1330 Dhan Group $1,290.00
3953 Admin Group 0
2650 Admin Group $364368.69
2651 Admin Group $1604546.97
Desired Output in history table
Hist_ID ID ORIGINAL_AMOUNT REVISED_AMOUNT
1 Dhan Group $4,800.00
2 Dhan Group $4,800.00 $7,470.00
3 Dhan Group $7,470.00 $9,270.00
4 Dhan Group $9,270.00 $10,560.00
5 Admin Group $0
6 Admin Group $0 $364,368.69
7 Admin Group $364,368.89 $1,968,915.86
=============================================================
USE [LEM]
BEGIN
DECLARE #Proj_RID nvarchar(255),
#Revised_Amount decimal(12,2),
#Original_Amount decimal(12,2)
DECLARE Running_Total CURSOR FOR
Select [ID], [Amount]
FROM [Transactions]
OPEN Running_Total;
FETCH NEXT FROM Running_Total into #Proj_RID, #Original_Amount;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Revised_Amount = #Revised_Amount+ #Original_Amount;
UPDATE HISTORY
SET REVISED_AMOUNT = #Revised_Amount,
Original_Amount = #Revised_Amount - #Original_Amount
WHERE [Project_ID] = #Proj_RID
FETCH NEXT FROM Running_Total into #Proj_RID, #Original_Amount;
END
CLOSE Running_Total;
DEALLOCATE Running_Total;
END
========================================================

Here is an example of how you can achieve this. Note that you need to use some ordering column as TransactionDate:
;WITH cte AS(SELECT *, (SELECT SUM(amount) FROM Transactions
WHERE id = t.ID AND TranDate < t.TranDate) AS ORIGINAL_AMOUNT
FROM Transactions t)
SELECT ID, ORIGINAL_AMOUNT, Amount + ISNULL(ORIGINAL_AMOUNT, 0) AS REVISED_AMOUNT
FROM cte
Here is complete script(but I use transaction ID as ordering column):
DECLARE #t TABLE
(
Tran_ID INT ,
ID NVARCHAR(100) ,
Amount MONEY
)
INSERT INTO #t
VALUES ( 1066, 'Dhan Group', 4800.00 ),
( 1327, 'Dhan Group', 2670.00 ),
( 1329, 'Dhan Group', 1800.00 ),
( 1330, 'Dhan Group', 1290.00 ),
( 2649, 'Admin Group', 0 ),
( 2650, 'Admin Group', 364368.69 ),
( 2651, 'Admin Group', 1604546.97 )
;WITH cte AS(SELECT *, (SELECT SUM(amount) FROM #t
WHERE id = t.ID AND Tran_ID < t.Tran_ID) AS ORIGINAL_AMOUNT
FROM #t t)
SELECT ID, ORIGINAL_AMOUNT, Amount + ISNULL(ORIGINAL_AMOUNT, 0) AS REVISED_AMOUNT
FROM cte
Output:
ID ORIGINAL_AMOUNT REVISED_AMOUNT
Dhan Group NULL 4800.00
Dhan Group 4800.00 7470.00
Dhan Group 7470.00 9270.00
Dhan Group 9270.00 10560.00
Admin Group NULL 0.00
Admin Group 0.00 364368.69
Admin Group 364368.69 1968915.66

Related

Alternative to subquery (mysql)

I have tables that hold payroll header & detail info on a separate table. Schema of each below,
CREATE TABLE `Payroll` (
`payId` int,
`groupId` int,
`startDate` date ,
`endDate` date,
`paymentDate` date
);
insert into Payroll values
(20,2,'2022-06-01','2022-06-30','2022-06-30'),
(21,2,'2022-07-01','2022-07-31','2022-07-31'),
(18,1,'2022-05-01','2022-05-31','2022-05-31'),
(19,1,'2022-07-01','2022-07-31','2022-07-31')
;
CREATE TABLE `PayrollItems` (
`payId` int NOT NULL,
`employeeId` int ,
`payCategory` varchar(45) ,
`value` decimal(15,4) NOT NULL
);
insert into PayrollItems values
(20,12,'salary',200),
(20,12,'housing',500),
(20,13,'salary',400),
(20,14,'salary',1300),
(21,12,'salary',200),
(21,12,'housing',500),
(21,13,'salary',400),
(21,14,'salary',1300),
(18,13,'salary',400),
(18,13,'housing',1300),
(19,14,'salary',500),
(19,14,'housing',1200)
;
I am trying to get a query wherein given a payid i should get the previous payid details. Previous payid is identified by a combination of the paymentDate & groupId fields.
Therefore, for the data above, for payid 19 i should get records of payid 18 i.e each pay item value, as they both are of the same groupid, 1 , and paymentDate of payid 18 is prior to paymentDate of payid 19. There could be more records that have a paymentDate dated prior to payid 19, but only first record dated prior is required.
I tried,
SELECT
y.*
FROM
Payroll ppi2
JOIN
PayrollItems prr3 ON (`prr3`.`payId` = `ppi2`.`payId`)
LEFT JOIN
(SELECT
prr3.employeeId,
ppi2.paymentDate,
ppi2.payId,
ppi2.groupId,
'Last months standard salary' AS Particulars,
MAX(CASE
WHEN TRIM(prr3.payCategory) = 'salary' THEN value
ELSE 0
END) salary,
MAX(CASE
WHEN TRIM(prr3.payCategory) = 'housing' THEN value
ELSE 0
END) housing
FROM
Payroll ppi2
JOIN PayrollItems prr3 ON (`prr3`.`payId` = `ppi2`.`payId`)
AND ppi2.payId = 19
GROUP BY ppi2.payId , prr3.employeeId , ppi2.paymentDate,ppi2.groupId
ORDER BY ppi2.paymentDate DESC) AS y ON (y.groupId = ppi2.groupId)
AND y.paymentDate < ppi2.paymentDate
GROUP BY y.payId,y.employeeId,y.paymentDate,y.groupId,y.Particulars;
but i am not getting any results.
Expected result,given payid = 19, would be,
payid employeeid housing salary
18 13 1300 400
Would there be another way of doing this ?
dbfiddle
WITH
cte AS (
SELECT t1.payid prev_payid
FROM Payroll t1
JOIN Payroll t2 USING (groupId)
WHERE t2.payid = #payid
AND t1.startDate < t2.startDate
ORDER BY t1.startDate DESC LIMIT 1
)
SELECT payId,
employeeId,
SUM(value * (payCategory = 'housing')) housing,
SUM(value * (payCategory = 'salary')) salary
FROM PayrollItems
CROSS JOIN cte
WHERE payid = prev_payid
GROUP BY 1, 2
https://dbfiddle.uk/1LAdyksH

SQL query for cumulative sum or subtract

how to create columns from unique values of a column and do a cumulative addition or subtraction.
I have a record of transactions like this
transaction_id
created_at
transaction_type
amount
124
2020-08-06 17:00:09
2
25.00
123
2020-08-06 17:00:03
1
50.00
There are various types of transactions, which in turn have different effects .Some results are withdrawals from th account, some are deposits and others don’t affect it at all. This information is summarized in another table (let’s call it ‘transaction_types’), as shown below:
id
description
effect
1
Manual Deposit
add
2
Direct Payment
subtract
id in the table transaction_type is a foreign key transactions. transaction_type = transactions_type.id
now if the initial amount is $10000 I need to create a table like this
transaction_id
initial_balance
deposit
withdrawal
final_balance
123
100000
50
100050
124
100050
25
100025
I don't know how to create new columns based on unique values in some columns also to start a cumulative sum from 10000. This is the query I tried select f.transaction_id, t.created_at, sum(case when d.effect = 'subtract' then -1 else 1 end * amount) from f inner join d on f.transaction_type = d.id
WITH RECURSIVE RowNums AS
(
SELECT
transaction_id,
created_at,
CAST(amount AS SIGNED) AS amount,
effect,
ROW_NUMBER() OVER
(
ORDER BY
created_at,
transaction_id
) AS RowNum
FROM transactions t
JOIN transaction_types tt
ON t.transaction_type = tt.id
),
Balances AS
(
SELECT
transaction_id,
created_at,
amount,
effect,
100000 AS initial_balance,
100000 +
(
CASE effect
WHEN 'add' THEN amount
WHEN 'subtract' THEN (-(amount))
END
) AS final_balance,
RowNum
FROM RowNums
WHERE RowNum = 1
UNION ALL
SELECT
rn.transaction_id,
rn.created_at,
rn.amount,
rn.effect,
b.final_balance,
b.final_balance +
(
CASE rn.effect
WHEN 'add' THEN rn.amount
WHEN 'subtract' THEN (-(rn.amount))
END
),
rn.RowNum
FROM Balances b
JOIN RowNums rn
ON (b.RowNum + 1) = rn.RowNum
)
SELECT
transaction_id,
initial_balance,
CASE effect
WHEN 'add'
THEN CAST(amount AS CHAR(20))
ELSE ''
END AS deposit,
CASE effect
WHEN 'subtract'
THEN CAST(amount AS CHAR(20))
ELSE ''
END AS withdrawal,
final_balance
FROM Balances
ORDER BY
created_at,
transaction_id
source : https://www.reddit.com/r/sqltutorial/comments/sx3ycb/comment/hxr6f9l/?utm_source=share&utm_medium=web2x&context=3

Aggregating query on Table?

I am stuck in one point i have to pick the data from the customer table which has values of customer_id and amount_paid . I want to show a result in the form that first 3 values of the user should be visible in a column name group as a text Group1 and 4 to 10 values of that user to get text Group2 and rest Group3 .
Can you please tell me how to group the values for every customer ?
Thanks
I want to show a result in the form that first 3 values of the user should be visible in a column name group as a text Group1 and 4 to 10 values of that user to get text Group2 and rest Group3
Below is for BigQuery Standard SQL
#standardSQL
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
-- ORDER BY user_id, grp
You can test / play with below dummy generated data
#standardSQL
WITH users AS (
SELECT user_id FROM UNNEST(GENERATE_ARRAY(1,5)) user_id
),
amounts AS (
SELECT ROUND(50 * RAND()) amount_paid FROM UNNEST(GENERATE_ARRAY(1,50)) amount_paid
),
customer AS (
SELECT user_id, ROUND(amount_paid * RAND()) amount_paid
FROM users
CROSS JOIN amounts
)
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
ORDER BY user_id, grp
The output will look like below
user_id grp amount_paid
1 1 147.0
1 2 323.0
1 3 879.0
2 1 147.0
2 2 323.0
2 3 879.0
. . .
so you still need calculate share which (from your question and hopefully) is not a problem for you
added share calculation
#standardSQL
WITH grps AS (
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
)
SELECT * ,
ROUND(amount_paid / SUM(amount_paid) OVER(PARTITION BY user_id), 3) share
FROM grps
-- ORDER BY user_id, grp

Ranking SUM of Value Column Grouped by Date

I have a query which I would like to add a ranking column. My existing query has three tables as a union query, with a sum of the total order value for that week. This query produces the sum of the total order value for that week, grouped by WeekCommencing, however I am struggling to add a ranking column based on the highest to the lowest total value for that week.
My (Updated) SQLFiddle example is here http://sqlfiddle.com/#!9/f1d43/35
CREATE and INSERT statements:
CREATE TABLE IF NOT EXISTS ORD (
WeekCommencing DATE,
Value DECIMAL(20 , 6 ),
Orders INT(6)
);
CREATE TABLE IF NOT EXISTS REF (
WeekCommencing DATE,
Value DECIMAL(20 , 6 ),
Orders INT(6)
);
CREATE TABLE IF NOT EXISTS SOH (
WeekCommencing DATE,
Value DECIMAL(20 , 6 ),
Orders INT(6)
);
INSERT INTO ORD (WeekCommencing, Value, Orders) VALUES
('2017-07-24',1,1),
('2017-07-31',2,1),
('2017-07-17',3,1);
INSERT INTO REF (WeekCommencing, Value, Orders) VALUES
('2017-07-24',4,1),
('2017-07-17',5,1),
('2017-07-31',6,1);
INSERT INTO SOH (WeekCommencing, Value, Orders) VALUES
('2017-07-17',7,1),
('2017-07-24',8,1),
('2017-07-31',9,1);
My best effort to date:
SELECT
WeekCommencing,
SUM(Value) AS 'TotalValue',
SUM(Orders) AS 'Orders',
#r:=#r+1 As 'Rank'
FROM
(SELECT
WeekCommencing, Value, Orders
FROM
ORD
GROUP BY WeekCommencing UNION ALL SELECT
WeekCommencing, Value, Orders
FROM
REF
GROUP BY WeekCommencing UNION ALL SELECT
WeekCommencing, Value, Orders
FROM
SOH
GROUP BY WeekCommencing) t1,
(SELECT #r:=0) Rank
GROUP BY WeekCommencing DESC;
My attempt currently ranks the order of week commencing, rather than the ranking highest to lowest.
My desired result is
WeekCommencing TotalValue Orders Rank
2017-07-31 17 3 1
2017-07-24 13 3 3
2017-07-17 15 3 2
Thanks is advance
SELECT a.*
, #i:=#i+1 rank
FROM
( SELECT weekcommencing
, SUM(value) totalvalue
, COUNT(*) totalorders
FROM
( SELECT weekcommencing, value, orders FROM ord
UNION ALL
SELECT weekcommencing, value, orders FROM ref
UNION ALL
SELECT weekcommencing, value, orders FROM soh
) x
GROUP
BY weekcommencing
) a
, (SELECT #i:=0) vars
ORDER
BY totalvalue DESC;

Difficult MySQL Query - Getting Max difference between dates

I have a MySQL table of the following form
account_id | call_date
1 2013-06-07
1 2013-06-09
1 2013-06-21
2 2012-05-01
2 2012-05-02
2 2012-05-06
I want to write a MySQL query that will get the maximum difference (in days) between successive dates in call_date for each account_id. So for the above example, the result of this query would be
account_id | max_diff
1 12
2 4
I'm not sure how to do this. Is this even possible to do in a MySQL query?
I can do datediff(max(call_date),min(call_date)) but this would ignore dates in between the first and last call dates. I need some way of getting the datediff() between each successive call_date for each account_id, then finding the maximum of those.
I'm sure fp's answer will be faster, but just for fun...
SELECT account_id
, MAX(diff) max_diff
FROM
( SELECT x.account_id
, DATEDIFF(MIN(y.call_date),x.call_date) diff
FROM my_table x
JOIN my_table y
ON y.account_id = x.account_id
AND y.call_date > x.call_date
GROUP
BY x.account_id
, x.call_date
) z
GROUP
BY account_id;
CREATE TABLE t
(`account_id` int, `call_date` date)
;
INSERT INTO t
(`account_id`, `call_date`)
VALUES
(1, '2013-06-07'),
(1, '2013-06-09'),
(1, '2013-06-21'),
(2, '2012-05-01'),
(2, '2012-05-02'),
(2, '2012-05-06')
;
select account_id, max(diff) from (
select
account_id,
timestampdiff(day, coalesce(#prev, call_date), call_date) diff,
#prev := call_date
from
t
, (select #prev:=null) v
order by account_id, call_date
) sq
group by account_id
| ACCOUNT_ID | MAX(DIFF) |
|------------|-----------|
| 1 | 12 |
| 2 | 4 |
see it working live in an sqlfiddle
If you have an index on account_id, call_date, then you can do this rather efficiently without variables:
select account_id, max(call_date - prev_call_date) as diff
from (select t.*,
(select t2.call_date
from table t2
where t2.account_id = t.account_id and t2.call_date < t.call_date
order by t2.call_date desc
limit 1
) as prev_call_date
from table t
) t
group by account_id;
Just for educational purposes, doing it with JOIN:
SELECT t1.account_id,
MAX(DATEDIFF(t2.call_date, t1.call_date)) AS max_diff
FROM t t1
LEFT JOIN t t2
ON t2.account_id = t1.account_id
AND t2.call_date > t1.call_date
LEFT JOIN t t3
ON t3.account_id = t1.account_id
AND t3.call_date > t1.call_date
AND t3.call_date < t2.call_date
WHERE t3.account_id IS NULL
GROUP BY t1.account_id
Since you didn't specify, this shows max_diff of NULL for accounts with only 1 call.
SELECT a1.account_id , max(a1.call_date - a2.call_date)
FROM account a2, account a1
WHERE a1.account_id = a2.account_id
AND a1.call_date > a2.call_date
AND NOT EXISTS
(SELECT 1 FROM account a3 WHERE a1.call_date > a3.call_date AND a2.call_date < a3.call_date)
GROUP BY a1.account_id
Which gives :
ACCOUNT_ID MAX(A1.CALL_DATE - A2.CALL_DATE)
1 12
2 4