Here is my query:
SET #rank=0;
SELECT #rank := #rank +1 AS rank_id, name, SUM(points) AS points
FROM battle_points
WHERE category = 'test'
AND user_id !=0
GROUP BY user_id
ORDER BY points DESC;
I'd like to add a column rank based on the total points. With this query, the points are fine but the rank_id virtual column doesn't match up.
For example, the top user with the most points has rank 26, yet the rank_id column has a value of 24.
How do I matchup the rank_id column with the points column?
Note: while I am fully versed in PHP, I need a solution for MySQL only.
You are on the right path, but you need to put the main query in a subquery so that the ordering occurs before the rank calculation, like so:
SET #rank=0;
SELECT #rank := #rank +1 AS rank_id, mainQ.*
FROM (
SELECT name, SUM(points) AS points
FROM battle_points
WHERE category = 'test'
AND user_id !=0
GROUP BY user_id
ORDER BY points DESC
) AS mainQ
;
Edit: Qualified * to mainQ.*.
Related
How to get ONLY the name of second (or nth) highest salaried person?
This is the query I have tried but this gives me only the name of highest salary paid:
SELECT emp_name FROM emp ORDER BY salary DESC LIMIT 1;
Not sure if that the best solution, but here is an example how you could do it:
SELECT * FROM (
SELECT customerName, length(customerName), #rownum := #rownum + 1 AS rank
FROM zenyatech.customer, (SELECT #rownum := 0) r
ORDER BY length(customerName)
) X WHERE rank = 2
You create a rank column first, and then use a query around that query and get only rank = 2 or N.
(The example is a little different, you would need to apply that to your table/database scenario)
This might help
SELECT name FROM employees ORDER BY salary DESC LIMIT 1,1
I have the following QUERY and I need only the sum of the best 28 results
SELECT id_person, sum(points)
FROM ranking
WHERE id_open>=847 and id_club=2 and id_person!='91'
GROUP BY id_person
ORDER BY sum(points) desc, id_ranking
Each player (id_person), in this serie (id_open=847 or more), in this club (id_club=2) can play about 56 games, but only 28 best result counts for ranking, in other words, I'll despise 28 worst results.
** EDITED QUERY ** (id_ranking isn't necessary in ORDER BY)
SELECT id_person, sum(points)
FROM ranking
WHERE id_open>=847 and id_club=2 and id_person!='91'
GROUP BY id_person
ORDER BY sum(points) desc
If I am understanding correctly...
Your goal: Display players (i.e. id_person) in DESC order based on how many points that person makes in his/her best 28 games.
Here is a query that potentially does that:
SELECT id_person, sum(points) as s
FROM (
SELECT id_person,
points,
#rownum := if(#person = id_person, #rownum + 1, 1) as pointRank,
#person := id_person as dummy
FROM (SELECT id_person, points
FROM ranking
WHERE id_open >= 847 and id_club = 2 and id_person != '91'
ORDER BY id_person, points DESC
) as q1,
(SELECT #rownum := 0) as r,
(SELECT #person := '') as s
) as q2
WHERE pointRank <= 28
GROUP BY id_person
ORDER BY s DESC;
SQL Fiddle (Note: leaves out q1's WHERE clause for convenience)
Subquery q1 explanation:
Filters out rows based on id_open, id_club, and id_person.
Then, orders based on id_person (implicitly ASC) and points (explicitly DESC).
Finally, selects the id_person and points fields.
Note: MySQL Group By's are a little special - aggregates will work, but if selecting non-aggregates, a random row will be selected for the group by, not all rows (MYSQL-Group-By-returns-only-first-row)
Subquery q2 explanation:
Adds in rownum and person variables to keep track of ranking of game. (generate-column-containing-record-index-in-table)
Needs to use the (SELECT #rownum := 0) as r and (SELECT #person := '') as s to set the variables. (These variables could be set outside too.)
if(#person := id_person, #rownum + 1, 1) is necessary to reset the ranking of games per person.
Note: Important to initialize the person variable to an empty string, as opposed to 0. Not too sure why, but if you do not, then it will not reset the rownum variable correctly.
Overall query explanation:
All row numbers (i.e. pointRank) less than or equal to 28 (basically, the 28 best point scores per id_person) are kept.
After this filtering, each id_person will be grouped again.
Finally, the top 28 games' points will be summed up and labeled as s, and these pairs (id_person,s) will be ordered from highest to lowest.
Here is another StackOverflow question with a good article link inside (highly recommended read): (limit-within-group)
Note: I cannot guarantee this will work though, since I do not know what your table looks like. That information would help.
I have a table in MySQL database as shown in the following snap shot.
I need to obtain a current row number based on rating_id (primary key) given in each group of products.
Currently the following SQL obtains a row number based on rating_id from all rows in the table.
SELECT rownum
FROM (SELECT #rownum := #rownum + 1 AS rownum,
tbl.rating_id
FROM rating tbl,
(SELECT #rownum := 0)t
ORDER BY tbl.rating_id DESC)t
WHERE rating_id = ?
How to restrict this query to a specific group of products (prod_id)?
In other words, how to get a row number from a group of rows which is specific a particular product (prod_id)?
I guess you need to count each group of PROD_ID separately. In this case you should add one more user defined variable to store previous PROD_ID and reset #rownum when new PROD_ID group starts.
SELECT rownum,rating_id,prod_id
FROM (SELECT if(prod_id=#prev_prod,#rownum := #rownum + 1,#rownum:=1)
AS rownum,
tbl.rating_id,
tbl.prod_id,
#prev_prod:=prod_id
FROM rating tbl,
(SELECT #rownum := 1,
#prev_prod:=NULL)t
ORDER BY tbl.prod_id)t
WHERE rating_id = ?
SQLFiddle demo
Sorry for posting another question about mysql ranking but all questions and answers which I already looked didn't help me....
I have mysql table of user points. User can have more results. My goal is to get max result from user and its rank.
CREATE TABLE results
(`user_id` int, `points` int);
INSERT INTO results VALUES
(1,10),
(2,20),
(3,20),
(4,30),
(4,60),
(5,5),
(1,80);
So upper solution would be:
rank | user_id | points
1 1 80
2 4 60
3 3 20
3 2 20
4 5 5
The following query does the trick:
SET #rank=0;
SET #points=0;
SELECT #rank := IF(#points = a.points, #rank, #rank + 1) AS rank, a.user_id, #points := a.points AS points
FROM (
SELECT user_id, MAX(points) as points
FROM results
GROUP BY user_id
) a
ORDER BY a.points DESC;
I have also created an SQLFiddle of it so you can see that it works: http://sqlfiddle.com/#!2/7ba2f/12
Use a user defined variable to produce the rank when selecting from an aggregated aliased query that calculates the maximum for each user:
select
(#rank := ifnull(#rank, 0) + 1) as rank,
user_id,
points
from (select
user_id,
max(points) as points
from results
group by 1
order by 2 desc) x
FYI, a UDV starts out life as null, hence the ifnull() call.
I have a MySQL SELECT query which uses 20 different comparisons within the same table. Here's an example:
SELECT * FROM mytable
WHERE (col1 > (col2 * 0.25))
AND (col5 < col10) .......
I'm trying to calculate percentile ranks based on the order of a column called SCORE within the SELECT results returned. I've tried using incremental row numbers and COUNT(*) to get the stock's rank and total number of results returned but not sure how to assign the same rank where some of the results have the same SCORE.
Here's the formula that I'm trying to calculate:
((COUNT(lower scores) + (COUNT(same/tied scores) / 2)) * 100) / COUNT(total results)
How do I find the number of lower scores, same/tied scores and total scores within the same result row for calculating percentiles on the fly?
I'm trying to avoid using stored procedures because I want to my application's admins to tailor the SELECT statement within my applications admin area as needed.
Using Shlomi's code above, here's the code that I came up with to calculate percentile ranks (in case anyone wants to calculate these in the future):
SELECT
c.id, c.score, ROUND(((#rank - rank) / #rank) * 100, 2) AS percentile_rank
FROM
(SELECT
*,
#prev:=#curr,
#curr:=a.score,
#rank:=IF(#prev = #curr, #rank, #rank + 1) AS rank
FROM
(SELECT id, score FROM mytable) AS a,
(SELECT #curr:= null, #prev:= null, #rank:= 0) AS b
ORDER BY score DESC) AS c;
Here's a post (of mine) which explains ranking during SELECT: SQL: Rank without Self Join.
It uses user defined variables which are accessed and assigned even as the rows are being iterated.
Using the same logic, it could be extended to include numbers of total scores, distinct scores etc. As a preview, here's a typical query:
SELECT
score_id, student_name, score,
#prev := #curr,
#curr := score,
#rank := IF(#prev = #curr, #rank, #rank+1) AS rank
FROM
score,
(SELECT #curr := null, #prev := null, #rank := 0) sel1
ORDER BY score DESC
;
The responses from Shlomi and Zishan (which uses Shlomi's code) definitely do not give accurate results, as I discovered by examining the results on a large table of mine. As answered elsewhere, it is apparently impossible to calculate percentile ranks in a single MySQL query:
SQL rank percentile
The Shlomi Noach approach using user-defined variables does - at first - look like it's working fine for the top couple percent of rankings, but it quickly degenerates for the lower-ranking rows in your table. Look at your data results for yourself, as I did.
See this blog post by Roland Bouman about why Shlomi's approach using user-defined variables within a single SQL statement doesn't work, with a proposed better solution:
http://rpbouman.blogspot.com/2009/09/mysql-another-ranking-trick.html
So then I adapted Bouman's code for this purpose and here's my solution, which necessarily combines PHP and MySQL:
Step 1) Calculate and store the absolute rank for each row by submitting the following two queries:
SET ##group_concat_max_len := ##max_allowed_packet;
UPDATE mytable INNER JOIN (SELECT ID, FIND_IN_SET(
score,
(SELECT GROUP_CONCAT(
DISTINCT score
ORDER BY score DESC
)
FROM mytable)
) AS rank
FROM mytable) AS a
ON mytable.ID=a.ID
SET mytable.rank = rank;
Step 2: Fetch the total number of rows (and store the result in a PHP variable $total)
SELECT COUNT(ID) FROM mytable
Step 3: Use a PHP loop to iterate through the table to use the absolute rank for each row to calculate the row's percentile rank:
3a) Loop through:
SELECT ID, rank FROM mytable
while storing those row values as $ID and $rank in PHP
3b) For each row run:
$sql = 'UPDATE mytable INNER JOIN (
SELECT (100*COUNT(ID)/'.$total.') percentile
FROM mytable
WHERE rank >= '.$rank.'
) a
ON mytable.ID = a.ID
WHERE mytable.ID='.$ID.'
SET mytable.percentile = a.percentile';
Probably not the most efficient process, but definitely accurate, and since in my case the 'score' value is not updated very often, so I run the above script as a cron batch operation to keep the percentile ranks up-to-date.