I'm trying to get the total number of levels gained or lost from this sort of table:
id name level timestamp
1 Rex 15 10:25
2 Rex 15 10:26
3 Rex 15 10:27
4 Rex 14 10:28
5 Rex 13 10:29
6 Rex 13 10:30
7 Rex 13 10:31
8 Rex 13 10:29
9 Xer 44 10:30
10 Xer 44 10:31
11 Xer 45 10:32
12 Xer 45 10:33
13 Xer 45 10:34
Currently I'm running
SELECT id, name, level, timestamp, MAX(level) - MIN(level) AS gained
FROM log
GROUP BY name
But the problem with this query is that both gained and lost levels will count as gained. It would be perfect if I could get a negative int in the gained column if the user has lost levels
The output I want from the data above is:
id name level timestamp gained
8 Rex 13 10:29 -2
13 Xer 45 10:34 1
If you need to respect the timeline, then try something like this:
SELECT MAX(id) id, name,
( SELECT level FROM log l0 WHERE l.name = l0.name ORDER BY timestamp DESC LIMIT 1 ) level,
MAX(timestamp) timestamp,
-- last entry for the name
( SELECT level FROM log l1 WHERE l.name = l1.name ORDER BY timestamp DESC LIMIT 1 ) -
-- first entry for the name
( SELECT level FROM log l2 WHERE l.name = l2.name ORDER BY timestamp ASC LIMIT 1 ) gained
FROM log l
GROUP BY name
I used LAG in as subquery to get the changes and then summed those changes in an outer sub-query. To get the last row I uses yet another query to find the max time for each name. Maybe not the most efficient query but it works
SELECT l.id, l.name, l.level, l.timestamp, sg.gain
FROM log l
JOIN (SELECT name, SUM(gain) gain
FROM (SELECT name, level - COALESCE(LAG(level) OVER w, level) as gain
FROM log
WINDOW w AS (PARTITION BY name ORDER BY timestamp)) as g
GROUP BY name) as sg ON sg.name = l.name
JOIN (SELECT name, MAX(time) max_t
FROM log
GROUP BY name) mt ON mt.name = l.name AND mt.max_t = l.time
Related
This question already has an answer here:
How to get the last row in the table using group by with Order by DESC?
(1 answer)
Closed 2 months ago.
For a Home-Automation Dashboard I am trying to display the latest values for different devices in different rooms.
My tables look like this
Table: devices
DeviceID
Floor
Room
Measurement
More Information
1
0
WC
Room-Temp
...
2
0
WC
Desired Room-Temp
...
3
0
WC
rel. Humidity
...
...
....
...
....
...
15
1
Kid
Room-Temp
...
16
1
Kid
Desired Room-Temp
...
17
1
Kid
rel. Humidity
...
Apart from that I have a table "messdaten" (=measurements) where I store the collected data (DeviceID+Timestamp is unique).
DeviceID
Timestamp
Value
3
2022-07-28 13:00:03
63
15
2022-07-28 12:58:37
21.3
17
2022-07-28 12:58:37
60
1
2022-07-28 12:55:00
20.8
...
...
...
What I would like to have now is a table in this form where I find all the latest data from each device for one specific room (here for example "WC").
DeviceID
Floor
Room
Measurement
Timestamp
Value
1
0
WC
Room-Temp
2022-07-28 12:55:00
20.8
2
0
WC
Desired Room-Temp
2022-07-28 12:53:00
20.5
3
0
WC
rel. Humidity
2022-07-28 13:00:03
63
My closest approach so far is:
SELECT devices.DeviceID, Floor, Room, Measurement, max(messdaten.Timestamp), messdaten.Value
FROM devices LEFT JOIN messdaten ON messdaten.DeviceID = devices.DeviceID
WHERE Room = (SELECT Room FROM devices WHERE DeviceID = 1)
GROUP BY devices.DeviceID
ORDER BY `devices`.`Measurement` ASC;
Unfortunately using "max" in Timestamp only applies on the column 'Timestamp' so that I get the latest timestamp but not the corresponding value.
On MySQL 8+, we can use ROW_NUMBER() here:
WITH cte AS (
SELECT d.DeviceID, d.Floor, d.Room, d.Measurement, m.Timestamp, m.Value,
ROW_NUMBER() OVER (PARTITION BY d.DeviceID ORDER BY m.Timestamp DESC) rn
FROM devices d
LEFT JOIN messdaten m ON m.DeviceID = d.DeviceID
WHERE Room IN (SELECT Room FROM devices WHERE DeviceID = 1)
)
SELECT DeviceID, Floor, Room, Measurement, Timestamp, Value
FROM cte
WHERE rn = 1
ORDER BY Measurement;
Using MariaDB and trying to see if I can get pull original rankings for each row of a table based on the create date.
For example, imagine a scores table that has different scores for different users and categories (lower score is better in this case)
id
leaderboardId
userId
score
submittedAt ↓
rankAtSubmit
9
15
555
50.5
2022-01-20 01:00:00
2
8
15
999
58.0
2022-01-19 01:00:00
3
7
15
999
59.1
2022-01-15 01:00:00
3
6
15
123
49.0
2022-01-12 01:00:00
1
5
15
222
51.0
2022-01-10 01:00:00
1
4
14
222
87.0
2022-01-09 01:00:00
1
5
15
555
51.0
2022-01-04 01:00:00
1
The "rankAtSubmit" column is what I'm trying to generate here if possible.
I want to take the best/smallest score of each user+leaderboard and determine what the rank of that score was when it was submitted.
My attempt at this failed because in MySQL you cannot reference outer level columns more than 1 level deep in a subquery resulting in an error trying to reference t.submittedAt in the following query:
SELECT *, (
SELECT ranking FROM (
SELECT id, RANK() OVER (PARTITION BY leaderboardId ORDER BY score ASC) ranking
FROM scores x
WHERE x.submittedAt <= t.submittedAt
GROUP BY userId, leaderboardId
) ranks
WHERE ranks.id = t.id
) rankAtSubmit
FROM scores t
Instead of using RANK(), I was able to accomplish this by with a single subquery that counts the number of users that have a score that is lower than and submitted before the given score.
SELECT id, userId, score, leaderboardId, submittedAt,
(
SELECT COUNT(DISTINCT userId) + 1
FROM scores t2
WHERE t2.userId = t.userId AND
t2.leaderboardId = t.leaderboardId AND
t2.score < t.score AND
t2.submittedAt <= t.submittedAt
) AS rankAtSubmit
FROM scores t
What I understand from your question is you want to know the minimum and maximum rank of each user.
Here is the code
SELECT userId, leaderboardId, score, min(rankAtSubmit),max(rankAtSubmit)
FROM scores
group BY userId,
leaderboardId,
scorescode here
I have an main dataset(users) as follows.
ID Username Status
1 John Active
2 Mike Active
3 Ann Deactive
4 Leta Active
5 Lena Active
6 Lara Active
7 Mitch Active
Further I have revenue table as follows.
subuser hour Revenue
John_01 2/26/2022 5:00 5
Mike_01 2/26/2022 7:00 8
Mike_02 2/26/2022 7:00 22
Leta_03 2/26/2022 7:00 67
Leta_07 2/26/2022 9:00 56
Mitch_07 2/26/2022 11:00 34
Now I need to get a table as follows.
User Total Usage
John 5
Mike 22
Leta 123
Lena 0
Lara 0
Mitch 0
Here I need to get the sum of all hours of each user substring and match with main user table.Further if same hour is for same substring I need to get the maximum revenue value and other values should be neglect for that particular hour.
Ex:
Mike_01 2/26/2022 7:00 8
Mike_02 2/26/2022 7:00 22
Here Mike_01 2/26/2022 7:00 8 should neglect.
So I tried as below.
SELECT
u.Username,
COALESCE(SUM(Revenue), 0) AS TOTAL USAGE
FROM users u
LEFT JOIN revenuetable e
ON SUBSTRING_INDEX(e.subuser, '_', 1) = u.Username AND
e.Hour BETWEEN 'XXX' and 'XXX'
where u.Status='Active'
GROUP BY
u.Username
order by u.ID.
But this didn't get the maximum value if same hour repeats. Can someone show me where I messed this?
update:
Do we have any method other tan using window functions?
If using MySQL that supports row_number() then join to a derived table that removes the unwanted rows.
SELECT
u.Username,
COALESCE(SUM(Revenue), 0) AS TOTAL USAGE
FROM users u
LEFT JOIN (
Select *
, row_number() OVER(partition by SUBSTRING_INDEX(e.subuser, '_', 1), hour order by revenue DESC) rn
From revenuetable ) e
ON SUBSTRING_INDEX(e.subuser, '_', 1) = u.Username AND rn = 1
e.Hour BETWEEN 'XXX' and 'XXX'
where u.Status='Active'
GROUP BY
u.Username
order by u.ID
Introducing this function and the over clause will give precedence to the highest revenue in each hour per user as the 'rn' column will be 1 for each such row.
I'm stuck on this query. I need to do a group by date, card_id and only show the highest hits. I have this data:
date card_name card_id hits
29/02/2016 Paul Stanley 1345 12
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Dave Mustaine 1349 10
25/02/2016 Ozzy 1351 17
23/02/2016 Jhonny Cash 1353 13
23/02/2016 Elvis 1355 15
20/02/2016 James Hethfield 1357 9
20/02/2016 Max Cavalera 1359 12
My query at the moment
SELECT DATE(card.create_date) `day`, `name`,card_model_id, count(1) hits
FROM card
Join card_model ON card.card_model_id = card_model.id
WHERE DATE(card.create_date) >= DATE(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND card_model.preview = 0
GROUP BY `day`, card_model_id
;
I want to group by date, card_id and filter the higher hits result showing only one row per date. As if I run a max(hits) with group by but I won't work
Like:
date card_name card_id hits
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Ozzy 1351 17
23/02/2016 Elvis 1355 15
20/02/2016 Max Cavalera 1359 12
Any light on that will be appreciated. Thanks for reading.
Here is one way to do this. Based on your sample data (not the query):
select s.*
from sample s
where s.hits = (select max(s2.hits)
from sample s2
where date(s2.date) = date(s.date)
);
Your attempted query seems to have no relationship to the sample data, so it is unclear how to incorporate those tables (the attempted query has different columns and two tables).
In MySql I need to select top student in every class in a school in termid=10 to get discount for next term enrollment .
Please notice that total is not in table(I put in below for clearing problem)
I have this workbook table for all students workbook:
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
2 1 22 40 20 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
5 7 22 10 40 50 10
6 8 11 10 30 40 10
7 9 33 20 45 65 10
8 11 11 null null null 10
9 12 54 null null null 02
10 13 58 null null null 02
1st challenge is : exam1 and exam2 are VARCHAR and total is not in table (as i explained).
2nd challenge is : as you can see in id=8 std #11 has not numbers
3rd challenge is : may be two students have top level so they must be in result.
I need result as :
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
2 1 22 40 20 60 10
i have this query but not work good as i mention.
SELECT DISTINCT id,studentid,classid,exam1,exam2,total,termid ,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) FROM workbook WHERE ClassId = '10';
You can get the total for the students by just adding the values (MySQL will convert the values to numbers). The following gets the max total for each class:
select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid;
You can then join this back to the original data to get information about the best students:
select w.*, coalesce(w.exam1, 0) + coalesce(w.exam2, 0) as total
from workbook w join
(select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid
) ww
on w.classid = ww.classid and (coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) = ww.maxtotal;
Another approach is to join the table with itself. You find out the max for each class and then join all students of this class which match the class max:
max for each class (included in the final statement already):
SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid
The complete statement:
SELECT s2.*, s1.maxtotal
FROM (SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid) s1
JOIN students s2 ON s1.classid = s2.classid
WHERE s1.maxtotal = (CAST(s2.exam1 AS UNSIGNED) + CAST(s2.exam2 AS UNSIGNED));
SQL Fiddle: http://sqlfiddle.com/#!2/9f117/1
Use a simple Group by Statement:
SELECT
studentid,
classid,
max(coalesce(exam1,0)) as max_exam_1,
max(coalesce(exam2,0)) as max_exam_2,
sum(coalesce(exam1,0) + coalesce(exam2,0)) as sum_exam_total,
termid
FROM
workbook
WHERE
termid=10
GROUP BY
1,2
ORDER BY
5
Try something like this:
SELECT id,studentid,classid,exam1,exam2,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) AS total,termid FROM `workbook` WHERE ((CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2)))) > 50
Thanks all my friends
I think combine between 2 answer in above is best :
SELECT s2.*, s1.maxtotal
FROM (SELECT ClassId, MAX(
coalesce(exam1,0)+
coalesce(exam2,0)
) as 'maxtotal'
FROM workbook
WHERE
(
termid = '11'
)
GROUP BY ClassId) s1
JOIN workbook s2 ON s1.ClassId = s2.ClassId
WHERE s1.maxtotal = (
coalesce(exam1,0)+
coalesce(exam2,0)
) AND (s1.maxtotal >'75');
last line is good for s1.maxtotal=0 (some times student scores have not be entered and all equals 0 so all will shown as best students) or some times we need minimum score (to enroll in next term).
So thanks all