I'm working on a track and field ranking database in MySQL/PHP5 whereby I'm struggling to find the best way to query results per unique athlete by highest value.
just
SELECT distinct name, event
FROM results
sample database
name | event | result
--------------------------
athlete 1 | 40 | 7.43
athlete 2 | 40 | 7.66
athlete 1 | 40 | 7.33
athlete 1 | 60 | 9.99
athlete 2 | 60 | 10.55
so let's say that in this case I'd like to rank the athletes on the 40m dash event by best performance I tried
SELECT distinct name, event
FROM results
WHERE event = 40
ORDER by result DESC
but the distinct only leaves the first performance (7.43) of the athlete which isn't the best (7.33). Is there an easy way other than creating a temp table first whereby the results are ordered first and performing a select on the temp table afterwards?
You might be interested in group by:
SELECT name, min(result) as result
FROM results
WHERE event = 40
GROUP BY name
This gives you the best result per athlete.
As suggested by spencer, you can also order the list by appending this:
ORDER BY min(result) ASC
The problem is that the columns used in the ORDER BY aren't specified in the DISTINCT. To do this, you need to use an aggregate function to sort on, and use a GROUP BY to make the DISTINCT work.
SELECT distinct name, event
FROM results
WHERE event = 40
GROUP BY name
ORDER by result DESC
Related
Take the following dataset:
id | Number
1 | 6534
1 | 765
1 | 1234
2 | 744
2 | 6109
3 | 333
3 | 9888
3 | 3112
3 | 98432
I want to show the highest Number for each id.
So Like this:
id | Number
1 | 6534
2 | 6109
3 | 98432
How can I do this with a SELECT statement?
I've already tried the following:
SELECT * FROM mytable ORDER BY id, Number Desc
But this shows the entire dataset.
I'm not trying to get the number of occurences. I am trying to get the highest Number grouped by id but can't get it to work.
SELECT id, MAX(Number) as Number FROM mytable GROUP BY id
You can try :
Select id,max(Number) from Your_Table group by id
Your Query that you have tried, it will only order your Data table by the given parameters.
In the meantime, What i have proposed to you, will select the two columns you want to diplay (the id, and the maximum of the column "Number").
And the group by will help to the maximum of each group. That's why a group by id is the right clause to have the maximum of each group of Ids.
Most of time the id field is incremental but for your case you can use.
SELECT MAX(number) FROM `user` GROUP BY id
Where number is the column name from which you want to find MAX, and user is your table name.
take the case you have 2 table, for example tbCostumers and tbOrders.
I would like to display a summary list with all costumers, related orders and display them with a paginator.
Doing a join I can extract costumers list and all orders for each costumer, the result is something like:
idCostumer | name | ... | idProduct | productName | price | ...
Where the first n columns are all equal if the costumer has more than 1 order. So I can have:
1 | will | ... | 12 | product1 | 123 | ...
2 | bill | ... | 23 | product2 | 321 | ...
2 | bill | ... | 24 | product3 | 231 | ...
And so on
I'm trying to use LIMIT to extract only n records and using them with a paginator.
First question: if a costumer has more than 1 order, with this query I'll see n records, equal in the first column (id, name, ... and other costumer info) but different at the end, where there are products info. Is this 'correct'? Is there another way to extract this informations?
Second question: if I do that and I use a LIMIT, I could "cut" the result table between 2 (or more) records that represent the same customer; so, for example in the small table above, if I limit with 2 the third row will be lost, even if it's part of the row above, because is just another order of the same costumer.
I would like to limit the number of different idCostumer, in order to take exactly n costumers, even if they appear more than 1 times in the result table. Something like n different idCostumer, no matter if they are repeated.
Is this possible?
I hope it's clear, it was not easy to explain what I would like to achieve :)
Thank you!
You might want to have something like this:
SELECT * FROM (
(SELECT * FROM tbCustomers LIMIT 3) AS c
INNER JOIN tbOrders AS o ON o.customer = c.idcustomer
);
You can substitute the first asterisk with named columns and only receive your desired columns in the order you prefer (ie: SELECT c.name, o.price FROM...) .
Hope this works for you!
EDIT: changing the value of the LIMIT clause changes the number of the picked customers, of course.
EDIT 2: As Alvaro Pointed out, you'll probably need an order clause in the tbCustomers query.
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 was just wondering that, how to get the greatest number in the table.
I mean i have a table called: hits; in that their are 2 columns:
1. id
2. hit
and their are many ids in the table and all have more than 10 hits, now what i want to do is to get the greatest id of the greatest hit
PS: See below:
id | hit
---|----
1 | 10
2 | 15
3 | 45
4 | 9
yes you can use MAX function to use like below
Select Id,Max(hit) from yourtableName group by id having hit=Max(hit)
Select Id,
Max(Hit)
from tableName
group by id
having Max(hit)=(Select Max(Hit) from TableName)
SQL FIDDLE Demo
Wouldn't it be faster to do this:
SELECT * FROM table WHERE 1 ORDER BY hit DESC, id DESC LIMIT 1
Rather than using MAX, Especially if you have a larger table
http://www.witti.ws/blog/2011/04/06/mysqls-max-slow-5-years-later
I am searching for records in a table as follows:
SELECT Id, Name FROM my_table WHERE Name LIKE '%prashant%' LIMIT 0, 10;
Now, I am adding LIMIT to maintain my paging. But when user searches for word 'prashant' then total records I have is 124 for 'prashant'. But as the limit applied to the query so it only fetches 10 records in my PHP script and when I am count the mysql variable in PHP code it returns total records found is 10.
So basically I want to count and Limit using a single query, by making some modification in the above query, I want the total count (124) of records. I don't want to run a separate count(*) query for just counting the total result found by the query.
Thanks.
SELECT SQL_CALC_FOUND_ROWS
Id, Name
FROM my_table
WHERE
Name LIKE '%prashant%'
LIMIT 0, 10;
# Find total rows
SELECT FOUND_ROWS();
more info
MySQL supports doing this using SQL_CALC_FOUND_ROWS as mentioned by Ionut. However, it turns out that in many cases it's actually faster to do it the old fashioned way using two statements, where one of them is a regular count(). This does however require that the counting can be done using an index scan, which won't be the case for this very query, but I thought I'd mention it anyway.
This is for others with the same need (considering it's been 3 years from the time of this question).
I had a similar issue and I didn't want to create 2 queries. So what I did was to create an additional column for the total number and moved the LIMIT and OFFSET clauses at the end:
SELECT SQL_CALC_FOUND_ROWS * FROM (
SELECT `id`, `name`
FROM `my_table`
WHERE `name` LIKE '%prashant%'
) res,
(SELECT /*CEIL(FOUND_ROWS()/10) AS 'pages',*/ FOUND_ROWS() AS 'total') tot
LIMIT 0, 10
So the result is something like
| id | name | total |
+-----+----------------+-------+
| 1 | Jason Prashant | 124 |
| 2 | Bob Prashant | 124 |
| 3 | Sam Prashant | 124 |
| 4 | etc. prashant | 124 |
and I think this solution has no disadvantage in timing because it fetches the result only once, and then uses the already calculated row count for the additional column.
In case of huge tables and selecting multiple fields (not just Id, Name as in your example) i would recommend to use 2 queries. Selecting count(0) first with all those WHERE clauses and only then build the pagination, selecting data etc.
It will work really faster on some popular eCommerce website, for example.
Don't use SQL_CALC_FOUND_ROWS and FOUND_ROWS()
there are the bugs reported
here: https://bugs.mysql.com/bug.php?id=18454
and here: https://bugs.mysql.com/bug.php?id=19553
while on small tables BENCHMARK is dependent more on other things and the resulting time your SELECT will take will be roughly the same as COUNT(0) - SQL_CALC_FOUND_ROWS still puts restraints on LIMIT and ORDER BY database optimizations so if you depend on them don't use SQL_CALC_FOUND_ROWS
on large tables the BENCHMARK difference becomes huge where a COUNT(0) might take 0.003 sec the same SQL_CALC_FOUND_ROWS might now take 20 sec
By all metrices COUNT(0) is the superior choice
COUNT(0) SYNTAX:
(SELECT COUNT(0) FROM tableNames WHERE condition) AS alias
// alias is optional but needed if you need to use the result later
So your total query would look like this
(SELECT COUNT(0) FROM my_table WHERE name LIKE '%prashant%') AS countResults;
SELECT Id, Name FROM my_table WHERE Name LIKE '%prashant%' LIMIT 0, 10;
And then just call countResults whereever you need it elsewhere...
Another advantage of using COUNT(0) is you can use it for searching both the same table as here or you can use it to search a different table...
So for instance if each person found also has 3, 2, 1, 5 diffenrent jobs respectively... you could just add a
(SELECT COUNT(0) FROM my_jobs_table WHERE name = name_row_in_jobs_table) AS jobs
inside your original SELECT like this
SELECT Id, Name (SELECT COUNT(0) FROM my_jobs_table WHERE name = name_row_in_jobs_table) AS jobs FROM my_table WHERE Name LIKE '%prashant%' LIMIT 0, 10;
Giving you:
--------------------
| id | Name | jobs |
--------------------
| 1 | Alice| 3 |
--------------------
| 2 | John | 2 |
--------------------
| 3 | Bob | 1 |
--------------------
| 4 | Jill | 5 |
--------------------
So when showing name[3] (ie. Bob) you could also show that Bob has 1 job by calling jobs[3]...