Get highest value from multiple columns and associated name column - mysql

I've got a table with these columns:
id | player1_name | player1_score | player2_name | player2_score | player3_name | player3_score | player4_name | player4_score | player5_name | player5_score
Given a single row, how do I get the highest playerX_score and the corresponding playerX_name?
I've tried using GREATEST(), but I can't get the playerX_name.

As an aside, I think your table would be better designed as id | name | score | position | teamid, where position goes from 1 to 5 and teamid serves to group everyone in the same team together. It would make this sort of query much easier (greatest-score-per-team with associated rows).
However, here's one way to do what you want with your current table:
SELECT GREATEST(player1_score,player2_score,player3_score,
player4_score,player5_score) as score,
CASE GREATEST(...) -- repeat the above
WHEN player1_score then player1_name
WHEN player2_score then player2_name
WHEN player3_score then player3_name
WHEN player4_score then player4_name
WHEN player5_score then player5_name
END as name
FROM mytable

I think your table structure isn't right for what you're trying to do. You want the database to know that there's some relationship between player1_name and player1_score, but that's not encoded in the table. A change that would make this much easier would be to give each player their own record, and use what you're currently calling id (which I assume is the ID for a particular game) to indicate which players go together.
It would look like this:
game_id | player_num | player_name | score
1 | 1 | Octern | 100
1 | 2 | Boris | 400
1 | 3 | Jarlsberg | 300
1 | 4 | Pete | 40000
...
Then, to find the high scorer for a given game (in this case, game #1), you'd say:
select player_name from scores
WHERE game_id = 1
ORDER BY score desc
LIMIT 1

Related

SQL ORDER BY using a value in another table using an equation grouped by duplicates

I have been given a movie database and I am trying to order by user rating held in a separate table to my movie information.
My problem is that my database does NOT have an overall rating of a movie, but only singular user ratings attached to that movie. But I want to order it by the overall average rating
For example:
SELECT Movies.title, Movies.movie_id, Movies.poster, Ratings.rating
FROM Movies INNER JOIN Ratings
ON Movies.movie_id = Ratings.movie_id
WHERE genre LIKE '%action%' AND (origin = 'american')
ORDER BY Ratings.rating DESC;
*(Rating is only selected to showcase to you the values, I do not need to use them outside this query)
This works out to display
+-------------------------+----------+------------------------------------+--------+
| title | movie_id | poster | rating |
+-------------------------+----------+------------------------------------+--------+
| The Baytown Outlaws | 2 | posters/The Baytown Outlaws.jpg | 5 |
| A Dark Truth | 8 | posters/A Dark Truth.jpg | 4 |
| A Dark Truth | 8 | posters/A Dark Truth.jpg | 3 |
| American Made | 14 | posters/American Made.jpg | 3 |
| Avengers: Age of Ultron | 4 | posters/Avengers Age of Ultron.jpg | 3 |
| Romeo Must Die | 1 | posters/Romeo Must Die.jpg | 3 |
| Avengers: Age of Ultron | 4 | posters/Avengers Age of Ultron.jpg | 2 |
| Fast & Furious 6 | 3 | posters/Fast & Furious 6.jpg | 2 |
| Olympus Has Fallen | 9 | posters/Olympus Has Fallen.jpg | 1 |
+-------------------------+----------+------------------------------------+--------+
Now from this I want to have my query select groups based on movie title, add up the ratings of that group, average that value, and then order titles by that average and excluding duplicate title names from the return
So my ideal query would return:
+-------------------------+----------+------------------------------------+
| title | movie_id | poster |
+-------------------------+----------+------------------------------------+
| The Baytown Outlaws | 2 | posters/The Baytown Outlaws.jpg |
| A Dark Truth | 8 | posters/A Dark Truth.jpg |
| American Made | 14 | posters/American Made.jpg |
| Romeo Must Die | 1 | posters/Romeo Must Die.jpg |
| Avengers: Age of Ultron | 4 | posters/Avengers Age of Ultron.jpg |
| Fast & Furious 6 | 3 | posters/Fast & Furious 6.jpg |
| Olympus Has Fallen | 9 | posters/Olympus Has Fallen.jpg |
+-------------------------+----------+------------------------------------+
So this returns my movie info ordered by average rating then excluding duplicate titles
Baytown Outlaws has 1 rating at 5 -> overall 5
Dark Truth has 2 ratings at 4 and 3 -> overall rating of 3.5 but only displays one row of movie info
American Made has 1 rating at 3 -> overall 3
etc.
I am having a lot of trouble figuring out this exact query, or if it is even possible. Any help or keyword suggestion would be useful as I am somewhat new to SQL and don't know all of its strengths. If not possible, I would also appreciate an answer saying so, so that I can go ahead and rework the database system to instead follow a better system of saving the overall rating within the Movies table.
A combination of COUNT(*) and SUM(rating) group by movie_id (or title).
Something like this:
SELECT Movies.title, Movies.movie_id, Movies.poster, SUM(Ratings.rating)/COUNT(*) AS avg_rating
FROM Movies INNER JOIN Ratings
ON Movies.movie_id = Ratings.movie_id
WHERE genre LIKE '%action%' AND (origin = 'american')
GROUP BY Movies.movie_id ORDER BY avg_rating DESC;
It can be done with the AVG function and a group by on multiple columns.
The tricky part of using AVG in that case is that when using such a function, every element in the SELECT part must either be in the GROUP or be an aggregate function. That being said, If you make groups by a combination of ID and Movie title, you would obtain the same result (in this case) as if you grouped by ID only.
You can use that to your advantage to add these columns in your SELECT section while using the AVG aggregate function.
In your first table, every time you look at the A Dark Truth movie, it comes with the same id, 8. Every time you look at the value Avengers: Age of Ultron, it comes with the same id, 4.
I suggest taking a few minutes and drawing a Venn diagram of the problem to get a good grasp of it as it seems this is material of a class.
I made a fiddle to demonstrate it for you. You can play around with it and add your initial join and where to complete it, I did a slight variation of the initial model, the create table is also in the fiddle.
SELECT Movie_id, Title, Poster, AVG(Rating)
FROM MoviesRatings
GROUP BY Movie_id, Title, Poster
ORDER BY AVG(Rating) DESC

SQL: many-to-many relationship and the 'ALL' clause

I have a table products and a table locations which are linked together in a many-to-many relationship with a table products_locations. Now a client can select a set of products, and I want to run a query that selects only the locations, where ALL of the selected products are available.
This seemed pretty straight forward at first, but I see myself being quite baffled by how to achieve this. I initially thought I could get all the correct location-ids with something like
SELECT location_id
FROM products_locations
WHERE product_id = ALL [the user selected product ids]
But on second thought that does not appear to make sense either (the structure of products_locations is quite simply [product_id, location_id].
Any suggestion on how to structure such a query would be appreciated. I feel like I am overlooking something basic..
EDIT: I am using mysql syntax/dialect
Quick sample: Given the following tables
| products | | locations | | products_locations |
| id | name | | id | name | | product_id | location_id |
|------------| |-----------| |--------------------------|
| 1 | prod1 | | 1 | locA | | 1 | 2 |
| 2 | prod2 | | 2 | locB | | 2 | 1 |
| 3 | prod3 | |-----------| | 2 | 2 |
|------------| | 3 | 1 |
|--------------------------|
If a user selects products 1 and 2, the query should return only location 2. If the user selects products 2 and 3, the query should return location 1. For 1, 2, and 3, no location would be valid, and for product 2, both locations would be valid.
I figured out a query that achieves what I need. Though it is not as clean as I had hoped, it seems to be a robust approach to what I'm trying to query:
SELECT t.location_id
FROM (SELECT location_id, COUNT(*) as n_hits
FROM products_locations
WHERE product_id IN [the user selected products]
GROUP BY location_id) t
WHERE n_hits = [the number of user selected products];
Explanation:
I create a temporary table t which contains every location_id that has at least one matching product in the user's selection, together with the number of times that location matches a product in the user's selection. This is achieved by grouping the query by location_id.
I select the location_id(s) from that temporary table t, where the number of hits is equal to the number of products the user had selected. If that number is lower, I know that at least one product did not match that location.

Get clan with bigger kills/deaths coef

I got table:
id | nick | clanid | kills | deaths | map
1 | xdd | 2 | 123 | 23 | 'map_1'
2 | xd | 1 | 23 | 32 | 'map_1'
this table continuing with similar records. I need to get only 1 result, it's should be clanid and coef(kills/deaths), of course i need the clan with higher coef. This table got many records with players which have different clanids, kills, deaths and map.
The complete result i need: it's clanid with higher coef at current map.I tried SQL like that:
SELECT `clanid`, SUM(kills)/SUM(deaths) as 'coef'
FROM `test_user_stats`
WHERE `map`='map_1'
But that returs only 1 record with last clanid and his coef.
What i have to do next?(i obviously need to use LIMIT 1 and ORDER BY coef, but i got problems even without going so far).

How to calculate "score" based on tags match

This will be a bit hard to explain. I have 3 tables, 1 for User, 1 for Interest & 1 for User_Interests. I want to calculate a score and add it to a new column. The score calculation is not that hard: Base points: 50. For each interest both users have I want to add 3 points to score, if a user has a interest that I don't have, I want to add 1 point to score. Is this possible using mySQL SELECT?
User:
| ID |
| 0 |
| 1 |
| 2 |
Interest:
| ID | title |
User_Interests:
| User_ID | Interest_ID |
This will however depends on my own ID. The end result I am looking is a list of all users and there score based on the tags from the user I supply. Let's say my user ID is 0:
Result:
| ID | score |
| 1 | 69 |
| 2 | 54 |
This LEFT JOIN lists common interests and interests of the other user that this user does not have.
SELECT
count(my.interest_id) AS common_interests,
count(*) AS all_interests
FROM users_interests ui
LEFT JOIN users_interest my ON
( my.user_id=$user_id
AND my.interest_id=ui.interest_id )
GROUP BY ui.user_id
The answer (according to the number of points you assign) would be common_interests*2 + all_interests

Mysql query Max not working

What i want to happen is group by parentid first, then group by position, which i have done. In that group i want the name with the highest rating to be displayed, which isn't happening. Instead the lowest id for each group is being displayed. The results should be tv1,tv3,tv5,tv7; as these are the highest rated values for each group.
id | name| parentid| position| rating |
1 | tv1 | 1 | 1 | 6 |
2 | tv2 | 1 | 2 | 5 |
3 | tv3 | 1 | 2 | 7 |
4 | tv4 | 1 | 2 | 3 |
5 | tv5 | 5 | 1 | 8 |
6 | tv6 | 5 | 1 | 2 |
7 | tv7 | 3 | 1 | 9 |
8 | tv8 | 3 | 1 | 3 |
$getquery = mysql_query("SELECT name,MAX(rating) FROM outcomes GROUP BY position,parentid") or die(mysql_error());
while($row=mysql_fetch_assoc($getquery)) {
$name = $row['name'];
$rating = $row['rating'];
echo "<p>Name: $name - $rating</p><p></p>";
}
It's not that the lowest id is being displayed -- you're not actually selecting the id column. Probably what you are seeing is the first entry in the name column for each group.
SELECT name, MAX(rating)
doesn't do what you think it does -- it doesn't instruct MySQL to pick the maximum value from the rating column, and also return the name that is associated with that row (aside: what do you think it would return if there was a tie for the maximum rating? What do you think it would return if you used AVERAGE rather than MAX?)
What it does instead is return the correctly calculated MAX(rating), and then one of the names out of that group. It doesn't guarantee which one gets returned, and it can change depending on how it decides to execute the query.
In fact, because of the undefined nature of a query such as this, it's not even legal SQL in other databases. (Try this in Postgres, and you'll get an error. Heck, try it in MySQL with the ONLY_FULL_GROUP_BY option enabled, and you'll get a similar error)
If what you want to do is find the maximum rating for each group, and then find the name associated with it, you'll have to do something like this:
SELECT name, max_rating FROM outcomes
JOIN (SELECT position, parentid, MAX(rating) AS max_rating from outcomes group by position, parentid) AS aggregated_table
USING (position, parentid)
WHERE rating = max_rating
(There are four or five other ways to do this, searching this site for mysql and aggregation will likely turn them up)