This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Mysql rank function
I have the following countryTable
country clicks
------- ------
0 222
66 34
175 1000
45 650
How do I get the ranking of say country 45 which is 2 in this case?
Ordered by country ASC:
SELECT 1+COUNT(*) AS ranking
FROM countryTable
WHERE country < 45 ;
Ordered by clicks DESC:
SELECT 1+COUNT(*) AS ranking
FROM countryTable AS t
JOIN countryTable AS c
ON c.clicks > t.clicks
WHERE t.country = 45 ;
You can get 2 rank as below it like below:
Select * from tabeName order by clicks limit 1,1
For 3 rank:
Select * from tabeName order by clicks limit 2,1
SELECT *
FROM
(
SELECT #ranking:= #ranking + 1 rank,
a.country,
a.clicks
FROM tableName a, (SELECT #ranking := 0) b
ORDER BY a.clicks DESC
) s
WHERE country = 45
SQLFiddle Demo
This will show the correct rank (2) for country 45. You don't specify how to rank ties, so you may want to change the comparison to suit you. Non existing countries rank as 0.
SELECT COUNT(*) rank
FROM countryTable a
JOIN countryTable b
ON a.clicks <= b.clicks
WHERE a.country = 45
SQLfiddle here.
X is the rank you need to look for:
SELECT * FROM T ORDER BY clicks DESC LIMIT X-1,1
Here's another (stunningly fast) way (albeit limited to 256 rows):
SELECT country
, clicks
, FIND_IN_SET(clicks,(SELECT GROUP_CONCAT(DISTINCT clicks ORDER BY clicks DESC) FROM country_clicks)) rank
FROM country_clicks
or, if you prefer...
SELECT FIND_IN_SET(clicks,(SELECT GROUP_CONCAT(DISTINCT clicks ORDER BY clicks DESC) FROM country_clicks)) rank
FROM country_clicks
WHERE country = 45;
Related
I have a table jackpot with columns uid for user ID and nright for number of right answers.
I manage to SELECT and rank users by right answers, but what next?
SELECT
a1.uid,
a1.nright,
COUNT(a2.nright) AS rank
FROM
jackpot a1,
jackpot a2
WHERE
a1.nright < a2.nright
OR (
a1.nright = a2.nright
AND a1.uid = a2.uid
)
GROUP BY
a1.uid,
a1.nright
ORDER BY
a1.nright DESC,
a1.uid DESC
I need to calculate the amount of points to give to each user depending on his position.
Only users with top 3 MAX nright receive points.
The total amount of points = the number of users*20.
First position gets 70% of the total, 2nd - 20%, 3rd - 10%.
In case of equal right answers between users, the points are split evenly (50/50, 33/33/33...).
SQL Fiddle
You need to decompose what you want.
1st step : You want the top 3 scores.
SELECT nright
FROM jackpot
ORDER BY nright DESC
LIMIT 3
2nd step : The user id who gets this 3 first scores
SELECT j.uid
FROM jackpot j
INNER JOIN (
SELECT nright
FROM jackpot
ORDER BY nright DESC
LIMIT 3 ) AS t ON t.nright = j.nright
3rd step: the total amount of point
SELECT COUNT(uid)*20 AS lot FROM jackpot
4th step: the rank and the number of person
Here we need to use a variable, as you are in php, you can't use set #var:= X; , so the trick is to do a Select #var:= X , this variable will not work because of the aggregate functions. So you need to do this :
SELECT #rank := #rank+1 as rank,T1.nright,T1.nb,T1.lot
FROM(
SELECT nright,
COUNT(uid) as nb,
(SELECT COUNT(uid)*20 FROM jackpot) as lot
FROM jackpot
GROUP BY nright
ORDER BY nright DESC
LIMIT 3
)T1, (SELECT #rank:= 0) r
5th step: The lots distribution
SELECT j.uid,
CASE
WHEN t.rank = 1 THEN (t.lot*0.7)/t.nb
WHEN t.rank = 2 THEN (t.lot*0.2)/t.nb
WHEN t.rank = 3 THEN (t.lot*0.1)/t.nb
END as lot
FROM jackpot j
INNER JOIN
(SELECT #rank := #rank+1 as rank,T1.nright,T1.nb,T1.lot
FROM(
SELECT nright,
COUNT(uid) as nb,
(SELECT COUNT(uid)*20 FROM jackpot) as lot
FROM jackpot
GROUP BY nright
ORDER BY nright DESC
LIMIT 3
)T1, (SELECT #rank:= 0) r) t ON t.nright = j.nright
I want to do the following. I have a table in the database, I am working on a table called asistencia and this table has 3 columns
id_asistencia as a int AUTOINCREMENT
nro_matricula as an int which I took it from another table called
alumnos
fecha as a date
This is a sketch of the database
id_asistencia | nro_matricula | fecha
1 | 0001| 2015-01-10
2 | 0002| 2015-01-10
3 | 0002| 2015-02-10 (another date )
The thing is I have to do a percentage
select all id_1 records in my nro_matricula column and see how many times its repeated in my rows and do a percentage respect all the dates in my database
EG : id_1 came to class day(whatever day) and he/she did not came to class the next day so id_1 has 50% assistance
Expected result
nro_matricula | percentage
0001| 50
0002| 100
The question is how can I make this query. If can be done in PHP its even better but i feel that this can be done in SQL
PS : The Database wasn't created by me
And excuse my English is not the better and i expect it to be understandable for you to help me
You can use sql statement like this:
select (
sum (if nro_matricula = '001' ,1,0 )
/ count(*)
from asistencia
--where nro_matricula = '001'
Maybe just simply:
select al.nro_matricula,
100 * count(distinct al.fecha) / (select count(distinct al1.fecha) from alumnos al1) as percentage
from alumnos al
group by al.nro_matricula
I did found the answer to my question. Thank you all for helping me out
SELECT
asistencia.nro_matricula as matricula,
COUNT( DISTINCT asistencia.fecha)* 100 /
COUNT( DISTINCT asistencia.nro_matricula) / (SELECT COUNT(DISTINCT asistencia.fecha)
FROM asistencia
ORDER BY COUNT(*) DESC
LIMIT 1 )
as porcentaje_asistencia
FROM asistencia
JOIN alumno
WHERE asistencia.nro_matricula = alumno.nro_matricula AND alumno.id_curso = 'basica6a'
Tried this in Oracle. Should work in MySQL too.
SELECT aa.NRO_MATRICULA , days_present/total_count* 100 FROM
(SELECT DISTINCT NRO_MATRICULA,
COUNT(*) as days_present FROM ASISTENCIA GROUP BY NRO_MATRICULA ) AA
,
(SELECT COUNT(*) as total_count FROM (SELECT DISTINCT(FECHA) FROM ASISTENCIA GROUP BY FECHA)) BB
Ouptut
nro_matricula percentage
0001 50
0002 100
The query (SELECT COUNT(*) FROM (SELECT DISTINCT(FECHA) FROM ASISTENCIA AA GROUP BY FECHA)) will give count of distinct date (2 in your case). Then we are getting distinct nro_matricula group by nro_matricula to get its count which will give the days it was present. Then divide both values from above steps to get percentage.
My problem is,
I have a table in mysql
which colums are
id Student score
1 A 55
2 B 86
3 C 65
4 D 23
5 E 84
6 F 45
7 G 80
I want to find rank of any student in whole class based on score, with the student who scored just greater them him and an another student who scored just less them him.
for example if I am searching for student E
then output should be
id User score rank_in_classs
2 B 86 1
5 E 84 2
7 G 80 3
An another example can be that if I am looking for student A
id User score rank_in_classs
3 c 65 4
1 A 55 5
6 F 45 6
How can I find it using mysql query.
Thanks
Query
SELECT id, Student, score,
FIND_IN_SET( score, (
SELECT GROUP_CONCAT( score
ORDER BY score DESC )
FROM tbl )
) AS rank_in_class
FROM tbl
ORDER BY rank_in_class
LIMIT 3;
DEMO
Using the FIND_IN_SET() based solution proposed by Ullas you can locate a particular student +/- 1 rank this way:
set #this_rank := (
SELECT
FIND_IN_SET( score, (
SELECT GROUP_CONCAT( score
ORDER BY score DESC )
FROM tbl
)
) AS rank_in_class
FROM tbl
where student = 'A'
);
select
*
from (
SELECT
id
, Student
, score
, FIND_IN_SET( score, (
SELECT GROUP_CONCAT( score
ORDER BY score DESC )
FROM tbl
)
) AS rank_in_class
FROM tbl
) ric
where rank_in_class between #this_rank-1 AND #this_rank+1
ORDER BY rank_in_class
LIMIT 3;
Note: IF you stored the rank value in the table, then this would not be so cumbersome and could perform way better.
The best way to do that is probably with a mix of two queries and a little of PHP.
The first query retrieves the rank of the "focused" student you want;
The second query retrieves the range of all the students around the focused "student" with a dynamic LIMIT statement (this is where the use of PHP is unavoidable);
So something like this would probably do the job :
$user = 'C'; // Student user we want to "focus"
$range = 1; // Range around the "focus" : 1 before and 1 after (could be changed to anything else)
// First query : retrieving the rank of the "focused" student
$stmt = $mysqli->prepare('SELECT COUNT(*) AS Rank FROM Student AS Focused INNER JOIN Student as Others ON Others.Score > Focused.Score OR (Others.Score = Focused.Score AND Others.Id > Focused.Id) WHERE Focused.user = ?');
$stmt->bind_param('s',$user);
$stmt->execute();
$res = $stmt->get_result()->fetch_assoc();
$startRank = $res['Rank'];
// Computing the dynamic LIMIT
if (($startRank - $range) < 1) {
$offset = 0;
$rowCount = $startRank + $range + 1;
} else {
$offset = $startRank - $range;
$rowCount = ($range * 2)+1;
}
// Second query : retrieving the rank of all the students around the "focused" student
$stmt = $mysqli->prepare('SELECT id, user, score, #curRank := #curRank + 1 AS rank FROM Student, (SELECT #curRank := ?) Rank ORDER BY Score DESC, id DESC LIMIT ?, ?');
$stmt->bind_param('iii',$offset,$offset,$rowCount);
$stmt->execute();
This is probably the most optimized way to query the database to get what you want. As a bonus, you can change the range to whatever you like.
Okay so I have a table that has the following
KEY username password score
The above columns are not in any specific order.
I want to send my Database a username and have it send me back what rank that user name is based on its score. So for example if I had 10 people in there and the 3rd person in has the highest score. When I pass the 3rd persons username in I want it to send back 1.
Is this possible?
I have been trying things like this
$result = mysql_query("SELECT * FROM tablename where username='$username' ORDER BY score DESC");
but it doesnt seem to give me the row number
This will handle ranks that have the same score.
SELECT d.*, c.ranks
FROM
(
SELECT Score, #rank:=#rank+1 Ranks
FROM
(
SELECT DISTINCT Score
FROM tableName a
ORDER BY score DESC
) t, (SELECT #rank:= 0) r
) c
INNER JOIN tableName d
ON c.score = d.score
// WHERE d.username = 'Helen'
SQLFiddle Demo (Ranking with duplicates)
SQLFiddle Demo (with filtering)
for example
KEY username password score Ranks
1 Anna 123 5 3
2 Bobby 345 6 2
3 Helen 678 6 2
4 Jon 567 2 4
5 Arthur ddd 8 1
for better performance, add an INDEX on column Score,
ALTER TABLE tableName ADD INDEX (Score)
SELECT
(SELECT COUNT(*)+1 FROM tablename WHERE score > t.score) as rank,
*
FROM
tablename t
where
username='$username'
The ORDER BY in your query is useless since you're only returning one row.
I have a user table with id, username and food_id columns. The idea is the user stored their favorite food and we come up with a league table of foods and I want to generate a report of the top votes for the each food type. I am using MySQL and PHP.
For clarity, here is an example of the table:
id food_id username
1 1 Bob
2 100 Jane
3 200 Andy
4 1 Maggy
5 100 Rich
6 100 Mick
7 1 Kevin
How do I write the query to list the foods that have the most votes. I want to limit the result to x number, say top 100. In this case I want the result to be as follows:
food_id score
1 3
100 4
I hope the question is clear enough. The query is beyond me but I am sure it must be possible to do it using DISTINCT and COUNT in some way or other. Perhaps it's sub queries?
select food_id, count(*) score
from myTable
group by food_id
order by score desc limit 100
SELECT F.food_name,
COUNT(*) AS score
FROM myTable AS M
INNER JOIN food_table AS F
ON F.food_id = M.food_id
GROUP BY F.food_name
ORDER BY score DESC limit 100
select count(*) as top100 from table group by food_id order by top100 desc limit 100
SELECT f.`food_id`, COUNT(*) AS `count`
FROM `fav_food_table` f
GROUP BY f.`food_id`
ORDER BY `count` DESC
LIMIT 100;