Ok hopefully my last post regarding the movie rating system I am trying to program. Thanks to user Asaph for helping me with my top 5 goring films. This is my database layout:
ID | UserID | Rating | TMDB | TYPE
-----------------------------------
1 34 6 432 2
-----------------------------------
2 34 9 432 3
-----------------------------------
3 44 9 468 2
and using this sql I can list the top gorey films (type 2)
sql SELECT `tmdb`, AVG(rating) AS avg_rating
FROM `tbl_rating`
WHERE `type`= :type
GROUP BY `tmdb`
ORDER BY avg_rating DESC
LIMIT 5
Now I was wondering is there a way I can make this more fair on my movie results?
for example movie123 might have 5 votes with an avg vote 7 and movie456 might only have 1 vote but if its above 7 it will be on the top of the list. How can i make this more fair? maybe somehow doing a min number of rows in group? or any other way?
Again any help is amazing!
Ok I think this is what I wanted to do :)
$sql = 'SELECT `tmdb`, AVG(rate) AS avg_rating, COUNT(rate) AS min_count FROM `tbl_rating` WHERE `type`= :type GROUP BY `tmdb` HAVING min_count > 1 ORDER BY avg_rating DESC LIMIT 5';
this seems to do the trick if there is a better way feel free to let me know :)
Related
I am trying to get a certain amount of rows of which another amount of rows satisfy a specific condition.
I'll explain.
table 1:
ID | NAME
1 | Thomas
2 | Jason
3 | Oleg
4 | Matt
5 | Sheldon
6 | Jenny
table 2:
ID | ACTIVE
1 | 1
2 | 0
3 | 1
4 | 1
5 | 0
6 | 1
Query:
SELECT tbl_1.ID, tbl_1.NAME, tbl_2.ACTIVE
FROM tbl_1 JOIN tbl_2 ON
tbl_1.ID = tbl_2.ID
WHERE tbl_2.ACTIVE=1
LIMIT 5
in this example I would like to get a minimum number of 5 users, of which 3 are active.
of course the query above will not do the job right, as it limits the total rows to 5. But 3 of the rows in the result (or less if no more exist) MUST be active.
the other way I can think of getting this done, is a union, but my query is so cumbersome, long and complex.
Any ideas?
Use ORDER BY instead:
SELECT tbl_1.ID, tbl_1.NAME, tbl_2.ACTIVE
FROM tbl_1 JOIN
tbl_2
ON tbl_1.ID = tbl_2.ID
ORDER BY (tbl_2.ACTIVE = 1) DESC
LIMIT 5;
This puts the active users at the top of the list and then fills in the rest with other users.
Note: The ORDER BY clause could simply be ORDER BY tbl_2.ACTIVE DESC. I left the boolean logic so you could see the similarity to the WHERE clause.
The way to at least x results is to use the count aggregate and the keyword having
select f1, count(*) records
from yourTable
where whatever
group by f1
having count(*) > x
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have a database with these tables
users(name, team_id, overall_user_score)
teams(id, name, team_score, totalscore)
How would I make a trigger to run after each update where its takes the score of all users in a team and the team_score of that team and add them together and puts it into totalscore.
I dont have any code for a trigger . I have the php to show the overall score and thats about it .
function showTeamScore() {
require "connect.php";
$score = mysqli_query($connection, "SELECT *
FROM teams
WHERE id = '".$_GET['id']."'") or die(mysqli_error($connection));
while ($data = mysqli_fetch_array($score)) {
echo $data['overall_score'];
}
}
Edit : Code and Problem update
mysqli_query($connection, "UPDATE teams SET totalscore=overall_score+IFNULL((SELECT sum(overall_user_score) FROM users WHERE team_id=id),0)") or die(mysqli_error($connection));
A simple subquery might do the trick:
-- query #1
SELECT id, tname,
(SELECT sum(uscore) FROM usr WHERE tid=id) tscore
FROM teams ORDER BY id
You can either run this select directly or define a view for it.
If you just want the total score you can simply do:
-- query #2
SELECT SUM(uscore) total_score FROM usr
Or, in combination with the previous team-list:
-- query #3 (combination of #1 and #2)
SELECT id, tname,(SELECT sum(uscore) FROM usr WHERE tid=id) tscore
FROM teams
UNION ALL
SELECT 999,'total score all teams',SUM(uscore) FROM usr
ORDER BY id
With sample data like - Demo, see here: http://www.sqlfiddle.com/#!9/9ba5b/4
teams:
id tname
1 Dallas
2 Houston
3 Austin
usr:
uid name tid uscore
1 Paul 1 10
2 Mary 1 3
3 Harry 2 7
4 Frank 2 4
5 Lisa 1 15
You would get this result from query #3:
id tname tscore
1 Dallas 28
2 Houston 11
3 Austin
999 total score all teams 39
OK, if you want to see all users with their respective team score in the last column you can do
-- query #4
SELECT uid, name,uscore,(SELECT sum(uscore) FROM usr WHERE tid=u.tid) tuscore
FROM usr u
which will result in
uid name uscore tuscore
1 Paul 10 28
2 Mary 3 28
3 Harry 7 11
4 Frank 4 11
5 Lisa 15 28
completely new answer:
You will probably need an update like this one (choose #4a or #4b):
-- query #4a
UPDATE teams t INNER JOIN
(SELECT tid, SUM(uscore) usc FROM usr GROUP BY tid) u ON u.tid=t.id
SET t.tsc=t.tsc+u.usc
or (see here http://www.sqlfiddle.com/#!9/040b2/1 ) :
-- query #4b (alternative version)
UPDATE teams SET tsc=tsc+IFNULL((SELECT sum(uscore) FROM usr WHERE tid=id),0)
-- IFNULL avoids the result to become NULL if there are no new contributions
Where the individual user contributions uscore will be added to the team total score tsc. It makes sense to have only one score column in teams.
With previous scores of
id tname tsc
1 Dallas 20
2 Houston 7
3 Austin 18
and the above listed user contributions the new total team scores will then be
id tname tsc
1 Dallas 48
2 Houston 18
3 Austin 18
After that the user scores should be reset to avoid a double counting. Do
UPDATE usr SET uscore=0
(Alternatively you could set an "invalidation-flag" in the user table, if you still want to be able to see the last score but not count it again.)
let me explain the whole thing with an example:
| id | product | rating |
1 23 54
2 23 54
3 23 53
4 24 33
5 26 22
6 24 11
Lets say we have multiple ratings for each product and want to display the three top products. This would mean we can can user Inner-/left-/right- Join to get the products name from another table, order it by desc and set a limit of 3. But this would show us the same product three times with a rating of 54, 54 and 53.
Is it possible to avoid products with the same id in the result just with SQL?
So the dream output from one SQL query would be:
| id | product | rating |
1 23 54
4 24 33
5 26 22
In words: the top three unique products by rating (and of course only the row of the item with the highest rating -> id 1 or 2 for product 23 and not id 3).
Further more if there is only one product or two products with multiple ratings it should only transfer 1 or 2 results.
You can do this by taking the maximum rating for each product and choosing the top three:
select product, max(rating) as maxrating
from table t
group by product
order by maxrating desc
limit 3;
If you want the id for this rating, you can use the substring_index()/group_concat() trick:
select product, max(rating) as maxrating,
substring_index(group_concat(id order by rating desc), ',', 1) as id
from table t
group by product
order by maxrating desc
limit 3;
Alternatively, you can eschew the group by:
select t.*
from table t
where not exists (select 1
from table t2
where t2.product = t.product and
(t2.rating > t.rating or
t2.rating = t.rating and t2.id > t.id
)
)
order by t.rating desc
limit 3;
The complicated where clause is because multiple ratings can be the same.
EDIT:
The not exists version is getting the highest rating on the highest id for each row. The logic is simply saying: "Get me all rows from the products table where the product in the row has no other row with a higher rating/id combination". This is an awkward way for people to understand "Get the row with the maximum rating". But it turns out to be easier for the database to process. It is typically the most efficient method in MySQL and often the most efficient method in other databases as well, particularly with the right indexes defined.
Use a SELECT DISTINCT query. Check out details here: http://dev.mysql.com/doc/refman/5.6/en/select.html
I have a list of films that users can rank in order of which they like best using jQuery UI Sortable (all works well). The lower the order number the better the film (1) and the higher (26) the worse it is. The list of films could be endless but is fixed in the database (users can't add more), so the user can only select from x list of films.
Films do not have to be in the users list, if they haven't seen film 5 then it won't get included (this may be compounding the problem).
Currently this is stored in the table:
film_id | user_id | order
4 2 3
5 3 3
6 2 1
7 2 2
7 3 1
8 3 2
What I want, and don't know where to start is an overall 'Top 10' style list. i.e. film 7 is the most popular because it appears higher up peoples lists and is in more lists. Film 6 could be the most popular but it's only in one list?!
I am stuck on both the logic and the Mysql queries to do it!
I am thinking I might need to weight the order somehow? Or have a separate table with the score per film and just update it after every edit. The following query seems like the right idea if it was just based on the count of items in the table but not when I want to add position in to the equation.
SELECT ff.film_id, COUNT(ff.film_id) AS cnt, SUM(ff.order) AS rank FROM
`favourite_film` AS ff GROUP BY ff.film_id ORDER BY cnt DESC, rank ASC
I guess I need the count of all the films in the table and the sum of the order (but reversed?), my theory then goes flat!
Any help or links would be greatly appreciated. Thanks.
Depending your "business rules", I think you should find some sort of calculation to both take into account the position and the number of "votes".
Just a random guess, but why not sorting by COUNT(votes)/AVG(pos) ? For maintainability reason, you might want to factor out the ranking function:
CREATE FUNCTION ranking(average_pos REAL, vote_count INT)
RETURNS REAL
DETERMINISTIC
RETURN vote_count/average_pos;
The query is now simply:
SELECT film_id,
AVG(pos) as a, COUNT(*) as c, ranking(AVG(pos),COUNT(*)) AS rank
FROM vote GROUP BY film_id
ORDER BY ranking(AVG(pos), COUNT(*)) DESC;
Producing with your example:
+----------+------+----+----------------+
| FILM_ID | A | C | RANK |
+----------+------+----+----------------+
| 7 | 1.5 | 2 | 1.333333333333 |
| 6 | 1 | 1 | 1 |
| 8 | 2 | 1 | 0.5 |
| 5 | 3 | 1 | 0.333333333333 |
| 4 | 3 | 1 | 0.333333333333 |
+----------+------+----+----------------+
See http://sqlfiddle.com/#!2/3b1d9/1
you should have reverted the list before saving it. this way you could leave the unselected movies out of the rating.
a workaround might be:
Count the amount of lists SELECT COUNT(DISTINCT(user_id) save this as $AMOUNT_OF_LISTS
now count the points using
SELECT film_id, (SUM(order)+($AMOUNT_OF_LISTS-COUNT(DISTINCT(user_id)))*POINTS_FOR_NOT_IN_LIST) as points FROM table GROUP BY film_id
logic: sum up all points and add POINTS_FOR_NOT_IN_LIST points for every time not in a list (total amount of lists - amount of times movie is in the list)
insert a value POINTS_FOR_NOT_IN_LIST to your liking. (might be 26 or 27 or even lower)
you probably want to add ORDER BY points DESC LIMIT 10 to the query to get 10 highest points
SELECT MIN( `order` ) , COUNT( * ) AS cnt, `film_id`
FROM `favourite_film`
GROUP BY `film_id`
ORDER BY cnt DESC , `order`
I would do this, I would assign a higher value to the movies with the higher ranking. Then I would sum the values per movie and order by the total descending to get the overall ranking. This way you are giving weight to both the popularity and rankings of each movie.
So if you wanted to do it by the top 3 ranked movies per user you could do this:
SELECT film_id, SUM(3 -- The max number of ranked movies per user
- order -- the ranking
+ 1) total_score
FROM TABLE_NAME
GROUP BY film_id
ORDER BY total_score DESC;
Obviously you could remove the comments
This way the top rated movie would get the higher score, the next highest, the next highest score, etc. If you were counting the top 10 movies per user, just change the 3 to 10.
I'm struggling to get my head around a query. I know how to do basic SQL but I'm a bit out of my depth with this one.
I'm trying to set up a query that returns a chart of highest ranked games. Users can rank a game out of 10. I want to select a list of the top 10 games and display them in a chart based on what their average rank is and also by the number of votes they have. So a game with an average rank of 8 and 20 votes will appear higher up the chart than a game with 10 votes and a average rank of 8.
If this can't be be done with pure SQL then I can always do some coding for the rest. At this stage its just about getting the data I need, and in the right format.
Any help would be much appreciated.
My table structure is as follows:
games
| id | title | platform | genre |
users
| id | email | username | password |
votes
| userid | gameid | vote |
The format of data I want to return is:
| title | platform | average rank | votes |
You could group by on games, and the rest is pretty simple:
select games.title
, games.platform
, games.genre
, avg(votes.vote) as AvgRank
, count(*) as VoteCount
from games
join votes
on votes.gamesid = games.gameid
join users
on users.id = votes.userid
group by
games.title
, games.platform
, games.genre
order by
avg(votes.vote) desc
, count(*) desc
limit 10
This query uses limit 10 to get the first 10 rows. If you were using SQL Server, you'd use select top 10 .... Oracle uses where rownum < 11.