MYSQL rating algorithm - mysql

I have a MYSQL table which stores a rating for each event of a user, 1-10 in the events table called event_rating. What i'm trying to find is the highest accurate rating based on the number of votes and rating like this:
SELECT
c.userid,
c.user_name,
COUNT(d.event_rating) AS votesCount,
AVG(d.event_rating) AS votesAvg,
SUM(d.event_rating) AS tsum,
COUNT(d.event_rating)*AVG(d.event_rating)/COUNT(d.event_rating) as totalRating
FROM events d JOIN users c ON d.userid = c.userid WHERE (d.userid = '2') GROUP BY d.userid ORDER BY totalRating DESC
I added two dummy ratings for a user, one with a rating of 10 and another with 5.
The results are below:
userid user_name votesCount votesAvg tsum totalRating
2 Rahul Khanna 2 7.5000 15 7.50000000
Is that accurate, and am i calculating it right?
--- EDIT ---
Posting Some more results
USING COUNT(d.event_rating)*AVG(d.event_rating)/COUNT(d.event_rating) as totalRating
userid user_name votesCount votesAvg tsum totalRating
2 Rahul Khanna 2 7.5000 15 7.50000000
1 Rita Agnihotri 9 4.8889 44 4.88888889
3 Daniel Springs 4 3.5000 14 3.50000000
4 Roger Myers 6 3.5000 21 3.50000000
5 Chun Tanakat 3 3.0000 9 3.00000000
USING COUNT(d.event_rating)*AVG(d.event_rating) as totalRating
userid user_name votesCount votesAvg tsum totalRating
1 Rita Agnihotri 9 4.8889 44 44.0000
4 Roger Myers 6 3.5000 21 21.0000
2 Rahul Khanna 2 7.5000 15 15.0000
3 Daniel Springs 4 3.5000 14 14.0000
5 Chun Tanakat 3 3.0000 9 9.0000
--- MORE ---
Should i be using the below instead?
COUNT(d.event_rating)*AVG(d.event_rating)/10 as totalRating
What i need is an accurate way to calculate the total rating, putting votesCount into consideration as well and that the rating has to be between 1-10.
--- MORE DETAIL ---
I have one table Users another Events, user is able to add new events and rate those events. The Events table has a eventID, userid, event_rating column. So each event can hold a rating for the event the user has created. What i want is to calculate all the events rating to get the max (total) rating for that user. I want the total rating i get to be between 1-10 and not exceed over 10 no matter how many events the user has created. Is that possible? if not any solutions or ideas?
Thanks for reading :)

What is this silly piece of query??
COUNT(d.event_rating)*AVG(d.event_rating)/COUNT(d.event_rating) as totalRating
This is nothing
(a*b)/a which is always b
you have to just fetch AVG(d.event_rating)
what do you want really ??

Related

Can this be done with a subquery?

I have a table looking like this:
Team Name Points
A Peter 26
A John 18
A Carl 20
A Robert 32
A Mike 10
B Tom 22
B Michael 28
B Tina 18
B Donald 35
B Jeff 20
I want to get a result from the query that will give me the best 3 users from a team and the SUM of the point from the 3 highest users.
For team A the 3 highest scores are Robert (32), Peter (26) and (Carl (20) which is a total of 78 points.
For team B the highest 3 scores are Donald (35), Michael (28) and Tom (22) which is a total of 85 points
So it must be something like this:
Place Team Points
1 B 85
2 A 78
I have tried something like this:
SELECT points, user FROM table ORDER BY points DESC, LIMIT 3
That will give me the 3 users with the highest points but I want also the SUM of these 3 users and these records must be per team.
I think it must be done with a subquery, is this correct?
SELECT team,SUM(points) sp FROM
(
(
SELECT * FROM `table` WHERE team = "A" ORDER BY points DESC LIMIT 3
)
UNION ALL
(
SELECT * FROM `table` WHERE team = "B" ORDER BY points DESC LIMIT 3
)
)
AS dbx
GROUP BY team
ORDER BY sp DESC
Explaination:
Your Data:
Get Highest 3 from both Team (A and B).
And then, sum After join them All.
Result:

SQL subquery in SELECT clause

I'm trying to find admin activity within the last 30 days.
The accounts table stores the user data (username, password, etc.)
At the end of each day, if a user had logged in, it will create a new entry in the player_history table with their updated data. This is so we can track progress over time.
accounts table:
id
username
admin
1
Michael
4
2
Steve
3
3
Louise
3
4
Joe
0
5
Amy
1
player_history table:
id
user_id
created_at
playtime
0
1
2021-04-03
10
1
2
2021-04-04
10
2
3
2021-04-05
15
3
4
2021-04-10
20
4
5
2021-04-11
20
5
1
2021-05-12
40
6
2
2021-05-13
55
7
3
2021-05-17
65
8
4
2021-05-19
75
9
5
2021-05-23
30
10
1
2021-06-01
60
11
2
2021-06-02
65
12
3
2021-06-02
67
13
4
2021-06-03
90
The following query
SELECT a.`username`, SEC_TO_TIME((MAX(h.`playtime`) - MIN(h.`playtime`))*60) as 'time' FROM `player_history` h, `accounts` a WHERE h.`created_at` > '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
Outputs this table:
Note that this is just admin activity, so Joe is not included in this data.
from 2021-05-06 to present (yy-mm-dd):
username
time
Michael
00:20:00
Steve
00:10:00
Louise
00:02:00
Amy
00:00:00
As you can see this from data, Amy's time is shown as 0 although she has played for 10 minutes in the last month. This is because she only has 1 entry starting from 2021-05-06 so there is no data to compare to. It is 0 because 10-10 = 0.
Another flaw is that it doesn't include all activity in the last month, basically only subtracts the highest value from the lowest.
So I tried fixing this by comparing the highest value after 2021-05-06 to their most previous login before the date. So I modified the query a bit:
SELECT a.`Username`, SEC_TO_TIME((MAX(h.`playtime`) - (SELECT MAX(`playtime`) FROM `player_history` WHERE a.`id` = `user_id` AND `created_at` < '2021-05-06'))*60) as 'Time' FROM `player_history` h, `accounts` a WHERE h.`created_at` >= '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
So now it will output:
username
time
Michael
00:50:00
Steve
00:50:00
Louise
00:52:00
Amy
00:10:00
But I feel like this whole query is quite inefficient. Is there a better way to do this?
I think you want lag():
SELECT a.username,
SEC_TO_TIME(SUM(h.playtime - COALESCE(h.prev_playtime, 0))) as time
FROM accounts a JOIN
(SELECT h.*,
LAG(playtime) OVER (PARTITION BY u.user_id ORDER BY h.created_at) as prev_playtime
FROM player_history h
) h
ON h.user_id = a.id
WHERE h.created_at > '2021-05-06' AND
a.admin > 0
GROUP BY a.username;
In addition to the LAG() logic, note the other changes to the query:
The use of proper, explicit, standard, readable JOIN syntax.
The use of consistent columns for the SELECT and GROUP BY.
The removal of single quotes around the column alias.
The removal of backticks; they just clutter the query, making it harder to write and to read.

Get most recent groups ordered by users last login date DESC

The title makes it sound easy but what I'd like to do is get the last 20 groups from the groups table, ordered by their corresponding users last login date. A group can have one or more users and ultimately what I want to do is find out which groups have had the least user activity and retrieve the last login date.
Here's the query I came up with -
SELECT DISTINCT g.name, user_max.max_login_last_at FROM groups g
LEFT JOIN group_user gu on g.id = gu.group_id
LEFT JOIN (
SELECT MAX(login_last_at) max_login_last_at, u.id
FROM users u GROUP BY id
) AS user_max ON (user_max.id = gu.user_id)
ORDER BY user_max.max_login_last_at ASC
The problem is when I perform the join, it pulls in every group_user record and results in duplicates. I feel like there could be an easy solution to this one but I can't seem to figure it out!
groups table
id name
1 Group 1
2 Group 2
users table
id email login_last_at
1 user1#example.com 2018-10-17 16:08:47
2 user2#example.com 2018-10-02 15:41:53
3 user3#example.com NULL
4 user4#example.com 2018-10-08 12:01:48
5 user5#example.com 2018-10-15 9:24:57
6 user6#example.com 2018-10-17 11:10:58
7 user7#example.com 2018-10-17 15:33:03
group_user table
id group_id user_id
1 1 1
2 2 1
3 1 2
4 1 3
5 1 4
6 2 5
7 1 5
8 2 6
9 1 7
Current example result -
name max_login_last_ts
Group 1 2018-10-02 15:41:53
Group 1 2018-10-08 12:01:48
Group 2 2018-10-15 09:24:57
Group 1 2018-10-15 09:24:57
Group 2 2018-10-17 11:10:58
Group 1 2018-10-17 15:33:03
Group 1 2018-10-17 16:08:47
Group 2 2018-10-17 16:08:47
Group 1 2018-10-18 08:55:17
The problem is as you can see in the result above is that I'm getting all groups, all I really want is the following -
name max_login_last_ts
Group 2 2018-10-17 16:08:47
Group 1 2018-10-18 08:55:17
Thanks in advance!
I think this query will do what you want. There isn't enough data in your sample to replicate your desired results though.
SELECT g.name, MAX(u.login_last_at) AS max_login_last_at
FROM `groups` g
JOIN group_user gu on gu.group_id = g.id
JOIN users u ON u.id = gu.user_id
GROUP BY g.name
ORDER By max_login_last_at DESC
LIMIT 20
Demo on dbfiddle

SQL : Fetching new data by date from mysql without iterating in program

I have a table called student with the structure below. the table represents student visitors to a library.
Id Name Date StudentId
1 John 2010-01-09 3
2 Matt 2010-01-10 5
3 Jane 2010-02-10 8
4 John 2010-02-10 3
5 Matt 2010-02-11 5
6 Jane 2010-02-11 8
7 Bob 2010-02-12 9
8 Tom 2010-02-12 10
9 Sam 2010-02-12 11
10 Jane 2010-02-12 8
I am running a query to fetch New visitors everyday. New visitors are visitors who have never visited the library before that date.The query I am using is as below.
SELECT count(*) FROM student
WHERE DATE(Date) = :date
AND Name NOT IN
(SELECT DISTINCT Name FROM student WHERE DATE(Date) < :date);
I am currently iterating over each date in the table in my program and then using the query above to fetch me New visitors everyday. This works fine but was wondering if it is possible to fetch all values in a single query without iterating in the program? what is more optimal?
the output I am expecting is as below:
Date Count of New Visitors
2010-01-09 1
2010-01-10 2
2010-01-11 0
2010-01-12 3
please note that the below query that I have tried will not fetch the correct result
SELECT Date, Count(*) FROM student
WHERE DATE(Date) BETWEEN :startdate AND :enddate
AND Name NOT IN
(SELECT DISTINCT Name FROM student WHERE DATE(Date) < :startdate) GROUP BY Date;
The result(which is incorrect and what I am not looking for ) I will get from the above query for startdate = 2010-01-09 and enddate = 2010-01-12 is below:
Date Count of New Visitors
2010-01-09 1
2010-01-10 3
2010-01-11 2
2010-01-12 4
You have error in your test output. I think this is what you are looking for:
select u1.date,
(select count(*)
from users u2
where u2.date = u1.date
and u2.studentid not in(select studentid from users u3 where u3.date < u2.date)) c
from users u1
group by u1.date

Sum values (same column)

I have this table
user_id time_completed
4 86.30887
5 57.81364
5 35.50281
5 10.00000
5 74.19355
5 31.91489
6 15.00000
6 20.50000
I need to sum all the time for each user, something like this:
user_id time_completed
4 86.30887
5 209.42489
6 35.50000
This is how I get the first table:
$result = mysql_query ("SELECT user_id,time_completed FROM `mytable` ORDER BY `mytable`.`user_id` ASC;" )
Any idea?
EDIT:
What if I need to replace user_id for the name in the following table (db_users)?
id username
1 admin
2 peter
3 tom
4 user
5 joey
6 helen
EDIT2:
I've modified this table (db_users) and I want country also appears in the query.
id username country
1 admin ES
2 peter IT
3 tom US
4 user GB
5 joey GE
6 helen FR
Like this:
user_id time_completed country
4 86.30887 GB
5 209.42489 GE
6 35.50000 FR
Take a look here: http://sqlfiddle.com/#!2/24d1b/11
you need to use SUM() which is an aggregate function and group them by their user_id
SELECT user_ID, SUM(time_COmpleted) totalTime
FROM tableName
GROUP BY user_ID
SQLFiddle Demo
UPDATE 1
SELECT b.username, SUM(time_COmpleted) totalTime
FROM tableName a
INNER JOIN db_users b
ON a.user_id = b.id
GROUP BY b.username
SQLFiddle Demo