update statement with aggregate function - mysql

Good day everyone..!:-)
I have this table tab where totalUsed is equal to the sum of all used values referenced to name
cid name used total
1 a 1 1
2 a 3 4
3 a 6 10
4 b 3 3
5 b 7 10
6 b 10 0
7 a 5 0
i have this code but it only copy totalUsed's adjacent used value
UPDATE tab
SET totalUsed=
(
SELECT SUM(used)
)
cid name used total
1 a 1 1
2 a 3 3
3 a 6 6
4 b 3 3
5 b 7 7
6 b 10 10
7 a 5 5
if used is set as 10 for cid 6, totalUsed should be 20
and for cid 7 it should be 15.
how to do it in mysql?
it should look like this.
cid name used total
1 a 1 1
2 a 3 4
3 a 6 10
4 b 3 3
5 b 7 10
6 b 10 20
7 a 5 15
thanks for help
:-)

In most dialects of SQL, you can do this using an update with a correlated subquery:
UPDATE tab
SET totalUsed = (SELECT SUM(used)
from tab tab2
where tab2.name = tab.name and
tab2.cid <= tab.cid
);
EDIT:
The above will not work in MySQL. You can do this instead:
UPDATE tab join
(select tab2.cid,
(SELECT SUM(used)
from tab tab3
where tab3.name = tab2.name and
tab3.cid <= tab2.cid
) as cum_used
from tab tab2
) tab2
on tab.cid = tab2.cid
SET tab.totalUsed = tab2.cum_used;

Related

Self-referential table ordering in SQL

Given a table like this:
taskId
nextTaskId
1
2
2
3
3
6
4
5
5
NULL
6
7
7
8
8
4
I need the following order for output:
taskId
nextTaskId
1
2
2
3
3
6
6
7
7
8
8
4
4
5
5
NULL
Is there any way to do this via MySQL query?
Using recursive CTE -
WITH RECURSIVE
self_ref_cte ( taskid, nexttaskid )
AS
( SELECT taskid, nexttaskid
FROM self_ref WHERE taskid = 1
UNION ALL
SELECT s.taskid, s.nexttaskid
FROM self_ref_cte c JOIN self_ref s
ON s.taskid = c.nexttaskid
)
SELECT * FROM self_ref_cte;
DB fiddle here.

MYSQL: Add a column to result which contains whether this rows contain max number of a particular column or not

I am using MySQL 5.7 and My Table is:
cp_id
cp_name
cp_version
cp_parent_id
1
playlist1
1
1
2
playlist1
2
1
3
playlist1
3
1
4
playlist2
1
4
5
playlist2
2
4
6
playlist3
1
6
7
playlist3
2
6
8
playlist3
3
6
9
playlist4
1
9
As you can see from the table that:
A single playlist can have more than one version but will have the same parent id.
Result I Require is:
I want to add a column to the result which contains whether that row is the cp version row or not.
cp_id
cp_name
cp_version
cp_parent_id
max_version
1
playlist1
1
1
0
2
playlist1
2
1
0
3
playlist1
3
1
1
4
playlist2
1
4
0
5
playlist2
2
4
1
6
playlist3
1
6
0
7
playlist3
2
6
0
8
playlist3
3
6
1
9
playlist4
1
9
1
Thanks, In Advance
On MySQL 8+ we can use MAX as an analytic function:
SELECT *, MAX(cp_version) OVER (PARTITION BY cp_parent_id) = cp_verson AS max_version
FROM yourTable
ORDER BY cp_id;
On earlier versions of MySQL we can use a join approach:
SELECT t1.*, t2.cp_version_max = t1.cp_version AS max_version
FROM yourTable t1
INNER JOIN
(
SELECT cp_parent_id, MAX(cp_version) AS cp_version_max
FROM yourTable
GROUP BY cp_parent_id
) t2
ON t2.cp_parent_id = t1.cp_parent_id
ORDER BY
t1.cp_id;

Query with CASE statement in Group By I need to create an entry where no results occur in the WHEN

I have a query which groups up incoming payments into date ranges (1-7 days, 3-6 months etc.) and it largely works as I had hoped. However, I want to return a row which says 0 when no income is expected in the date range.
The group by looks like this:
group by
CASE WHEN timestampdiff(day,curdate(),data.duedate) between 0 and 7 then 1
WHEN timestampdiff(day,curdate(),data.duedate) between 8 and 14 then 2
WHEN timestampdiff(day,curdate(),data.duedate) between 15 and 30 then 3
WHEN timestampdiff(month,curdate(),data.duedate) between 1 and 2 then 4
WHEN timestampdiff(month,curdate(),data.duedate) between 2 and 3 then 5
WHEN timestampdiff(month,curdate(),data.duedate) between 3 and 6 then 6
WHEN timestampdiff(month,curdate(),data.duedate) between 6 and 12 then 7
WHEN timestampdiff(year,curdate(),data.duedate) between 1 and 2 then 8
WHEN timestampdiff(year,curdate(),data.duedate) between 2 and 3 then 9
WHEN timestampdiff(year,curdate(),data.duedate) between 3 and 4 then 10
WHEN timestampdiff(year,curdate(),data.duedate) between 5 and 6 then 11
WHEN timestampdiff(year,curdate(),data.duedate) >= 7 then 12
This works correctly in that it will give me the correct amounts, but I want to force the code to give me a 0. So I currently get this:
1 300000
5 150000
8 300000
What I actually want is this:
1 300000
2 0
3 0
4 0
5 150000
6 0
7 0
8 300000
etc.
This is the entire query - I've tried using an IFNULL() but had no success:
select
sum(data.principaldue+data.interestdue) as m
from
(select
la.id
,rep.duedate
,rep.PRINCIPALDUE
,rep.INTERESTDUE
from repayment rep
join loanaccount la on la.ENCODEDKEY = rep.PARENTACCOUNTKEY
join loanproduct lp on lp.ENCODEDKEY = la.PRODUCTTYPEKEY
group by
CASE WHEN timestampdiff(day,curdate(),data.duedate) between 0 and 7 then 1
WHEN timestampdiff(day,curdate(),data.duedate) between 8 and 14 then 2
WHEN timestampdiff(day,curdate(),data.duedate) between 15 and 30 then 3
WHEN timestampdiff(month,curdate(),data.duedate) between 1 and 2 then 4
WHEN timestampdiff(month,curdate(),data.duedate) between 2 and 3 then 5
WHEN timestampdiff(month,curdate(),data.duedate) between 3 and 6 then 6
WHEN timestampdiff(month,curdate(),data.duedate) between 6 and 12 then 7
WHEN timestampdiff(year,curdate(),data.duedate) between 1 and 2 then 8
WHEN timestampdiff(year,curdate(),data.duedate) between 2 and 3 then 9
WHEN timestampdiff(year,curdate(),data.duedate) between 3 and 4 then 10
WHEN timestampdiff(year,curdate(),data.duedate) between 5 and 6 then 11
WHEN timestampdiff(year,curdate(),data.duedate) >= 7 then 12
END
Order by
CASE WHEN timestampdiff(day,curdate(),data.duedate) between 0 and 7 then 1
WHEN timestampdiff(day,curdate(),data.duedate) between 8 and 14 then 2
WHEN timestampdiff(day,curdate(),data.duedate) between 15 and 30 then 3
WHEN timestampdiff(month,curdate(),data.duedate) between 1 and 2 then 4
WHEN timestampdiff(month,curdate(),data.duedate) between 2 and 3 then 5
WHEN timestampdiff(month,curdate(),data.duedate) between 3 and 6 then 6
WHEN timestampdiff(month,curdate(),data.duedate) between 6 and 12 then 7
WHEN timestampdiff(year,curdate(),data.duedate) between 1 and 2 then 8
WHEN timestampdiff(year,curdate(),data.duedate) between 2 and 3 then 9
WHEN timestampdiff(year,curdate(),data.duedate) between 3 and 4 then 10
WHEN timestampdiff(year,curdate(),data.duedate) between 5 and 6 then 11
WHEN timestampdiff(year,curdate(),data.duedate) >= 7 then 12
END
This is not a complete answer, but would be too big for comments;
A temporary table with numbers could be useful:
MySql temporary tables:
CREATE TEMPORARY TABLE TempTable (num int);
INSERT INTO TmpTable VALUES(1,2,3,4,5,6,7,8,9,10,11,12 ...);
Then you could right join on this table to make sure missing values are included.
Lets say you have this:
results(num, val):
1 300000
5 150000
8 300000
This should result in your desired output:
SELECT numbers.num, COALESCE(results.val, 0) as val
FROM results RIGHT JOIN TempTable numbers on results.num = numbers.num
WHERE numbers.num <= 12 --or other max number
1 300000
2 0
...
5 150000
...
Hope this helps.
Edit:
If you don't have permission to create temporary tables, look for a workaround to select consecutive integers, for example:
SELECT #row := #row + 1 as row, t.*
FROM some_table t, (SELECT #row := 0) r
Where some_table is any table with enough rows.
Probably use a top N on that.
Another dirty workaround, might be good enough if you don't need many numbers:
SELECT 1 num
UNION
SELECT 2 num
UNION
...
Edit:
Slightly tidier workaround:
SELECT * FROM (VALUES (1), (2), (3), ... ) x(i)

How to calulate SUM previous and current row value in MySQL table?

My table is "Activity_table", which have 4 columns. How can I create a function, which SUM each Peroson 2 previous and current activities?
My Activity_table
ID PID AID Act
1 1 1 12
2 1 2 32
3 2 1 5
4 1 3 21
5 2 2 12
6 2 3 19
7 1 4 11
8 2 4 6
PID-PersonID; AID-ActivitieID; Act-Activitie Value
My target:
ID PID AID Act SUM
1 1 1 12 12
2 1 2 32 44
3 2 1 5 5
4 1 3 21 65
5 2 2 12 17
6 2 3 19 36
7 1 4 11 64
8 2 4 6 37
Sum1=12; Sum2=32+12; Sum3=5; Sum4=21+32+12; Sum5=12+5; Sum6=19+12+5; Sum7=11+21+32; Sum8=6+19+12; Thank you,
To use the two previous and the current row,
SELECT ID,PID,AID,Act,
(SELECT SUM(Act)
FROM Activity_table
WHERE ID <= a.ID
AND ID >= a.ID - 2
AND PID = a.PID)
FROM Activity_table a;
You are taking the SUM according to the PID right? Then the last two rows of your target should be modified as,
7 1 4 11 **76**
8 2 4 6 **42**
The most flexible query is for your requirement is,
SELECT ID,PID,AID,Act,
(SELECT SUM(Act)
FROM Activity_table
WHERE ID <= a.ID
AND PID = a.PID)
FROM Activity_table a;
Then if you need only the flow of SUM of a particular PID, you can change it like this,
SELECT ID,PID,AID,Act,
(SELECT SUM(Act)
FROM Activity_table
WHERE ID <= a.ID
AND PID = a.PID)
FROM Activity_table a
WHERE PID = 2;
Result:
ID PID AID Act SUM
3 2 1 5 5
5 2 2 12 17
6 2 3 19 36
8 2 4 6 42
Sorry for the previous wrong Answers (deleted).
This produces the correct answer based on your example:
SET #LastPID = 0, #Act1 = 0,#Act2 = 0,#Act3 = 0;
SELECT ID,PID,AID,Act,`SUM` FROM
(
SELECT ID,PID,AID,Act,#Act3 := IF(#LastPID != PID, 0,#Act2),#Act2 := IF(#LastPID != PID, 0,#Act1), #Act1 := Act, #Act1 + #Act2 + #Act3 `SUM`, #LastPID := PID
FROM Activity_table
ORDER BY PID,ID
) sm
ORDER BY ID
;

Min Max Value in coloumn but with a certain condition

I have a MySql table with 3 coluomn : Nip, Bidang and Total.
I want to count min and max value of total with the same bidang. but i don't want to count min and max value for all coloumns.
Sample data:
NIP Bidang Total
1 A 10
2 A 5
3 A 1
4 B 4
5 B 7
6 C 8
7 C 9
And the result column:
MIN
1
1
1
4
4
8
8
Simply do this:
SELECT * FROM
(
(SELECT Bidang FROM TableName) T1 LEFT OUTER JOIN
(SELECT bidang, MIN(Total) MinVal, MAX(Total) MaxVal
FROM TableName
GROUP BY Bidang) T2 ON T1.Bidang=T2.Bidang
)
Result:
BIDANG MINVAL MAXVAL
A 1 10
A 1 10
A 1 10
B 4 7
B 4 7
C 8 9
C 8 9
See result in SQL Fiddle.
EDIT
To see the Total column also, add Total to first query.
SELECT * FROM
(
(SELECT NIP,Bidang,Total FROM TableName) T1 LEFT OUTER JOIN
(SELECT bidang, MIN(total) MinVal, MAX(Total) MaxVal
FROM TableName
GROUP BY Bidang) T2 ON T1.Bidang=T2.Bidang
)
Result:
NIP BIDANG TOTAL MINVAL MAXVAL
1 A 10 1 10
2 A 5 1 10
3 A 1 1 10
4 B 4 4 7
5 B 7 4 7
6 C 8 8 9
7 C 9 8 9
See result in SQL Fiddle.
Select bidang, min(total) MyMin, Max(total) myMax
From tableName
group by bidang
NIP bidang total
1 A 10
2 A 5
3 A 1
4 B 4
5 B 7
6 C 8
7 C 9
should return
A 1 10
B 4 7
C 8 9