MySQL: Conditional MIN() with GROUP BY - mysql

I have this table called times where I record race information for a racing game:
race_id map name time
30509 desert Peter 12.68
30510 desert Jakob 10.72
30511 desert Peter 18.4
30512 jungle Peter 39.909
30513 jungle Peter 39.84
30514 desert Harry 16.129
30515 space Harry 774.765
30516 jungle Jonas 46.047
30517 city Jonas 23.54
30518 city Jonas 23.13
30519 desert Mike 22.9
30520 space Fred 174.244
I have two questions. How would I best go about:
Finding the lowest time (world record) on a given map?
I have tried this query:
SELECT *, MIN(time) FROM times WHERE map = 'desert';
This yields a seemingly incorrect arbitrary row with an added column called MIN(time) where the correct lowest time is.
Finding the lowest time on all maps, but only if it's done by a certain player (find all world records by given player)?
For this I have tried this query:
SELECT *, MIN(time) FROM times WHERE name = 'Peter' GROUP BY map;
This seems to only return the first row by the given name for each map, regardless if it's the lowest time or not.
I'm fairly new to SQL(MySQL), so I might be missing something obvious here. I've been looking around for quite a while now, and any help would be greatly appreciated. Thanks!

if you want the fastest performance on a given race, you can just order by and limit:
select *
from times
where map = 'desert'
order by time limit 1
On the other hand, if you want all race records for a given user, then it is a bit different. One option uses a correlated subquery for filtering:
select t.*
from times t
where
name = 'Peter'
and time = (select min(t1.time) from times t1 where t1.map = t.map)

Finding the lowest time (world record) on a given map
SELECT `time`
FROM times
WHERE map = #map
ORDER BY `time` ASC
LIMIT 1
Finding the lowest time on all maps, but only if it's done by a certain player (find all world records by given player)
SELECT `time`
FROM times
WHERE name = #name
ORDER BY `time` ASC
LIMIT 1

Related

Mysql SUM CASE with unique IDs only

Easiest explained through an example.
A father has children who win races.
How many of a fathers offspring have won a race and how many races in total have a fathers offspring won. (winners and wins)
I can easily figure out the total amount of wins but sometimes a child wins more than one race so to figure out winners I need only sum if the child has won, not all the times it has won.
In the below extract from a query I cannot use Distinct, so this doesn't work
SUM(CASE WHEN r.finish = '1' AND DISTINCT h.runnersid THEN 1 ELSE 0 END ) AS winners,
This also won't work
SUM(SELECT DISTINCT r.runnersid FROM runs r WHERE r.finish='1') AS winners
This works when I need to find the total amount of wins.
SUM(CASE WHEN r.finish = '1' THEN 1 ELSE 0 END ) AS wins,
Here is a sqlfiddle http://sqlfiddle.com/#!2/e9a81/1
Let's take this step by step.
You have two pieces of information you are looking for: Who has won a race, and how many races have they one.
Taking the first one, you can select a distinct runnersid where they have a first place finish:
SELECT DISTINCT runnersid
FROM runs
WHERE finish = 1;
For the second one, you can select every runnersid where they have a first place finish, count the number of rows returned, and group by runnersid to get the total wins for each:
SELECT runnersid, COUNT(*) AS numWins
FROM runs
WHERE finish = 1
GROUP BY runnersid;
The second one actually has everything you want. You don't need to do anything with that first query, but I used it to help demonstrate the thought process I take when trying to accomplish a task like this.
Here is the SQL Fiddle example.
EDIT
As you've seen, you don't really need the SUM here. Because finish represents a place in the race, you don't want to SUM that value, but you want to COUNT the number of wins.
EDIT2
An additional edit based on OPs requirements. The above does not match what OP needs, but I left this in as a reference to any future readers. What OP really needs, as I understand it now, is the number of children each father has that has run a race. I will again explain my thought process step by step.
First I wrote a simple query that pulls all of the winning father-son pairs. I was able to use GROUP BY to get the distinct winning pairs:
SELECT father, name
FROM runs
WHERE finish = 1
GROUP BY father, name;
Once I had done that, I used it is a subquery and the COUNT(*) function to get the number of winners for each father (this means I have to group by father):
SELECT father, COUNT(*) AS numWinningChildren
FROM(SELECT father, name
FROM runs
WHERE finish = 1
GROUP BY father, name) t
GROUP BY father;
If you just need the fathers with winning children, you are done. If you want to see all fathers, I would write one query to select all fathers, join it with our result set above, and replace any values where numWinningChildren is null, with 0.
I'll leave that part to you to challenge yourself a bit. Also because SQL Fiddle is down at the moment and I can't test what I was thinking, but I was able to test those above with success.
I think you want the father name along with the count of the wins by his sons.
select father, count(distinct(id)) wins
from runs where father = 'jack' and finish = 1
group by father
sqlfiddle
I am not sure if this is what you are looking for
Select user_id, sum(case when finish='1' then 1 else 0 end) as total
From table
Group by user_id

SELECT the oldest of the most recent lines

I have a table storing the scores (with the date) of players they did at each game.
Example:
john 154 10/02/2014
mat 178 09/02/2014
eric 270 08/02/2014
mat 410 07/02/2014
john 155 06/02/2014
In this example I want "eric 270 08/02/2014" because thins is the oldest of the most recents.
Which request must I do to retrieve that ?
As I understand it, you request the oldest entry among the set containing the most recent one of each user.
In such a case, you can deal with your problem using a subquery given the last date for each user, then used in the main query to select and sort only the most recent entry of each user.
SELECT scores.*
FROM scores
INNER JOIN
(
SELECT max(date) last, name
FROM scores
GROUP BY name
) last_temp_table
ON scores.name = last_temp_table.name
AND scores.date = last_temp_table.last
ORDER BY scores.date ASC LIMIT 1;
More info in different SO threads such as MySQL order by before group by
The question as worded doesn't make much sense unless you define most recent.
If I assume that you have some criteria like: "Give the oldest event that happened within the last 3 days" then that is a simple matter of ordering and limiting across a date range.
select * from events where ts >= CURDATE() - 3
order by ts asc
limit 1

Count, max, and multiple sub querys SQL

I'm currently working on a league systeme for my sport team. A ladder, as seen as in some video games.
It's a mobile web site, allowing coaches to create games, and monitor players performances.
I have games automatically balanced, taking into accounts player's experiences and points, then, i give bonus points to the all the players of the winner team, and remove points from the losers.
I have a relatively simple database. 3 tables.
User : id - name
Games : id - ETA - cration_date
game_joueur: id- id_game - id_joueur - team - result - bonus
game_joueur beeing an assoc table, in wich i register for each new game players id, the team he has been seeded on, and afterwards, update the bonus field with the points earned and the result field with an integer (1 = lose, 2= win)
That way i can sum the bonus on my players stat and get the total points.
You can have a better look at the table here :
http://sqlfiddle.com/#!2/d3e06/2
What i'm tryng to acomplish is for each player's stat page, retrieve from the database the name of his most succesfull partner( the guy wich whom he won the most games), and also his worst ally , the men he lost the most match with.
This is what i do on my user stat page :
SELECT
(SELECT COUNT(lad_game_joueur.result) FROM lad_game_joueur WHERE result = 1 AND lad_game_joueur.id_joueur = lad_user.id) as lose,
(SELECT SUM(lad_game_joueur.bonus) FROM lad_game_joueur WHERE lad_game_joueur.id_joueur = lad_user.id) as points,
lad_user.id as id ,
(SELECT COUNT(lad_game_joueur.result) FROM lad_game_joueur WHERE lad_game_joueur.id_joueur = lad_user.id AND result =2) as win,
lad_user.name
FROM lad_user,lad_game_joueur
WHERE lad_game_joueur.id_joueur = lad_user.id AND lad_user.id
='.$id_joueur.'
GROUP BY lad_user.id
ORDER BY puntos DESC
I'm sure this is not the best way to do it, but it works :) ( i'm no sql specialist)
How can i tune this query to also retrive the informations i'm looking for?
I wont mind doing another query.
Thanks a lot in advance!
Ben
Ok i finealy found a way.
Here's what i did :
SELECT
SUM(result)as result_sum, sum(Bonus) as bonus_sum, id_joueur
from lad_game_joueur
where result= 2
and id_game in
(SELECT lad_game_joueur.id_game from lad_game_joueur,lad_game where id_joueur=2
AND result= 2 and lad_game_joueur.id_game=lad_game.id)
group by id_joueur
order by result_sum DESC, bonus_sum desc
As you see, the sum of result would give me 4 if i won two games with the person, but i just divide by 2 on php and voilĂ  :)

SQL Query to provide me with the total number

I have a table with 3 columns
Column 0: auto inc index
Column 1: Person's Gener
Column 2: The STATE the person lives in (US States and Puerto Rico)
I want to run a query that tells me how many MEN are in each state
so the output would list all 50 states (in 1 column) and the second would be a number to determine the number of MEN in that state
Alaska 1000
New York 85000
(the figures above aren't accurate but i'm illustrating what I am looking for)
Thanks
You need to use a GROUP BY clause and the COUNT aggregate function.
SELECT State, COUNT(*) AS NumberOfMen
FROM your_table
WHERE Gender = 'M'
GROUP BY State
try this
select STATE, count(*) as num from table_name where gender ='MALE' GROUP BY STATE;

In MySQL, can I have a table returning the ten last rated games by rating?

The actual question is a little more complex than that, so here goes.
I have a website which reviews games. Ratings/reviews are posted for each game, and so I have a MySQL database to handle it all.
Thing is, I'd really like a page that showed what score (out of 10) meant what, and to illustrate it would have the game that was last reviewed as an example. I can always do it without, but this would be cooler.
So the query should return something like this (but running from 10 to 0):
|---------------*----------------*-----------------*-----------------|
* game.gameName | game.gameImage | review.ourScore | review.postedOn *
|---------------*----------------*-----------------*-----------------|
| Top Game | img | 10 | (unix timestamp)|
| NearlyTop Game| img | 9 | (unix timestamp)|
| Great Game | img | 8 | (unix timestamp)|
|---------------*----------------*-----------------*-----------------|
The information is in two tables, game and review. I think you'd use MAX() to find out the last timestamp and corresponding game information, but as far as complex queries go, I'm in way over my head.
Of course this could be done with 10 simple SELECTs but I'm sure there must be a way to do this in one query.
Thanks for any help.
Here is an ugly solution I found:
This query simply gets the IDs and scores of the reviews that you want to look at. I have included it so that you can understand what the trick is, without getting distracted by other stuff:
SELECT * FROM
(SELECT reviewID, ourScore FROM review ORDER BY postedOn DESC) as `r`
GROUP BY ourScore
ORDER BY ourScore DESC;
This exploits MySQL's 'GROUP BY' behavior. When the grouping is done, if the source rows have different values for different columns, then the value of the topmost source row is used. So if you had rows in this order:
reviewId Score
1 3
0 3
2 3
Then after you group by score, the reviewId is 1 because that row was on the top:
reviewId Score
1 3
So we want to put the most recent review on the top before we do the group by. Since ORDERing is always dones after grouping, in a single SELECT statement, I had to make a subquery to accomplish this. Now we just dress up this query a little bit to get all the fields you wanted:
SELECT `r`.*, game.gameName, game.gameImage FROM
(SELECT reviewID, ourScore, postedOn, gameID FROM review ORDER BY **postedOn DESC**) as `r`
JOIN game ON `r`.gameID = game.gameID
GROUP BY ourScore
ORDER BY ourScore DESC;
That should work.
SELECT DISTINCT game.gameName, game.gameImage, review.ourScore FROM game
LEFT JOIN review
ON game.ID = review.gameID
ORDER BY review.postedOn
LIMIT 10
Or something like that, check out how to use the Distinct first, I'm not sure on the syntax, and you may have to tell the ORDER BY DESC or ASC depending on what you want.
Well..
SELECT game.gameName, game.gameImage, review.ourScore
FROM game
LEFT JOIN review ON game.gameID = review.gameID
GROUP BY review.ourScore DESC
LIMIT 10
returns a list of games grouped by each individual score. But this isn't what I want, I want the game that is last posted - this is why the timestamp is important. With that query, MySQL returns the first result it can find.
I think this would work:
select g.gameName, g.gameImage, r.ourScore, r.postedOn
from game g, review r
where g.gameId = r.gameId
and r.postedOn = (select max(sr.postedOn)
from review sr where sr.ourScore = r.ourScore)
group by r.ourScore
order by r.ourScore desc;
Edit: above SQL was corrected after David Grayson's comment. I think this query is pretty easy to understand but probably performs poorly compared with his solution.