I have list of hospitals under that there are average ratings already calculated. Now I wanted to calculate rank for list of hospitals according to their average ratings from following query
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r
ORDER BY currentAvgRating DESC
Now above query works when I want to see all hospitals from table but when I apply WHERE clause like below then result is wrong since with this it takes row position.
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r where hospitalID = '453085'
ORDER BY currentAvgRating DESC
Is there any way to get correct result when we apply where clause?
If you proceed what you just found out, logically ("when there is only 1 listitem, it cannot be ordered") - you will come to the conclusion that you NEED to select ALL rows. But nothing wrong with that, you can pack them into a subselect (which isnt even an expensive one) and apply the WHERE to that:
SELECT * FROM (
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r
ORDER BY currentAvgRating DESC
) toplist
WHERE toplist.hospitalID = 453085
Wrap in a subquery.
SELECT * FROM (
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r
ORDER BY currentAvgRating DESC
)
WHERE hospitalID = '453085'
Related
Trying to rank positions but instead of giving (1,2,3,3,5..) when there is a tie in the 3rd position it gives (1,2,3,4,5...).. Help please
below is the code.. thanks
.............................................................................
SELECT t.*
FROM (SELECT #curRank := IF(#prev= #cur, #curRank, #curRank + 1 ) AS classPosition,student_id, #prev:=#cur, #cur:=SUM(total_marks)
FROM (SELECT m.*
FROM marks m
WHERE classform_name = ? AND term = ? AND academic_year = ? GROUP BY student_id
ORDER BY SUM(total_marks) DESC
) n CROSS JOIN
(SELECT #curRank := 0, #prev:=NULL, #cur:=NULL ) q GROUP BY student_id
) t
WHERE student_id = ?
.............................................................................
You don't need a RANK function. You can always rank rows by joining the table to itself, in which case you get to choose how the order (and rank) is determined.
I've mixing session/# variables and aggregation can be a bit unreliable, but you could simply try changing the order of your select expressions to
student_id, #cur:=SUM(total_marks), #curRank := IF(#prev= #cur, #curRank, #curRank + 1 ) AS classPosition, #prev:=#cur
If this doesn't help, I'd suggest separating your aggregation and ranking. Actually, you're already summing in the subquery, so I am not quite sure why you're not just including the sum in the results from n.
SELECT * FROM (
SELECT #curRank := IF(#prev= #cur, #curRank, #curRank + 1 ) AS classPosition, student_id, #prev:=#cur, #cur:=overall
FROM (SELECT m.*, SUM(total_marks) AS overall
FROM marks m
WHERE classform_name = ? AND term = ? AND academic_year = ?
GROUP BY student_id
ORDER BY overall DESC
) AS n
CROSS JOIN (SELECT #curRank := 0, #prev:=NULL, #cur:=NULL ) AS q
) AS completeRankings
WHERE student_id = ?
Actually, your original query should've had other issues anyway. n would have only included a single random total_marks value for each student_id; making the outer SUM kind of pointless.
Edit - This should allow position progression to "skip" later positions according to ties:
SELECT #curRank := #curRank + 1 AS counter, #prevRank := IF(#prev=#cur, #prevRank, #curRank) AS classPosition
... to omit counter from the final results, you'll have to expand out the * explicitly.
Ranks are a real pain in MySQL.
If you want the rank for a single student with these conditions, I would recommend:
SELECT 1 + COUNT(*)
FROM marks m
WHERE m.classform_name = ? AND m.term = ? AND m.academic_year = ? AND
m.total_marks >= (SELECT m2.total_marks
FROM marks m2
WHERE m2.classform_name = m.classform_name AND
m2.term = m.term
m2.academic_year = m.academic_year
m2.student_id = ?
);;
I am little new to sql and I need to rank my votes table based on votes assigned to each person and rank should be same for the similar votes.
My table would be like
CREATE TABLE votes ( name varchar(10), votes INT );
INSERT INTO votes VALUES
('Ann',100), ('Jones',151), ('Smith',100), ('Rose',240), ('Lee',500), ('Adhams',500);
In my display rows I need to have the rank column first and it should display the rank based on the highest number of votes. Importantly same number of votes need to have the same rank.
I have tried it several times and failed to do it...
Please help me
thanks
You can try below code. There are many links available which would be easily find your answer if you have carefully searched.
SET #rank=0;
SET #votes=0;
select x.rank as rank, x.name as name, x.votes as votes
from(
select #rank:=if(#votes=votes,#rank, #rank +1)AS rank,
#votes := votes,
v.name,
v.votes
from votes v
order by v.votes desc) as x;
To generate RANK, you first need a row number in the order of decreasing votes (variable #rn) and then based on the previous value of vote, create rank (variable #rank).
Try this:
SELECT v.*,
#rank := if((#rn := #rn + 1) is not null,
if (#votes = votes,
#rank,
if ((#votes := votes) is not null, #rn, 1)
),1
) rank
FROM votes v
CROSS JOIN (
SELECT
#votes := NULL,
#rank := 0,
#rn := 0
) t
ORDER BY v.votes DESC;
Demo
About this:
(#rn := #rn + 1) is not null
Since, the expression #rn := #rn + 1 can't be null, we use it to our advantage by not duplicating the whole logic twice.
You can use variables for this:
SELECT #rnk := IF(#v = votes, #rnk,
IF(#v := votes, #rnk + 1, #rnk + 1) AS rnk
name, votes
FROM mytable
CROSS JOIN (SELECT #rnk := 0, #v = :0) AS vars
ORDER BY votes DESC
My Table gainfinal consists of three columns-countrycode, year and values. I want to select ten rows with top ten values. First, I created a rank according to values with the following Query.
SELECT countrycode, `values`,
#curRank := #curRank + 1 AS rank
FROM gainfinal CROSS JOIN
(SELECT #curRank := 0) vars
WHERE year = 2000
ORDER By `values` DESC ;
Now, I need to select the top ten rows with the highest rank. How can I do it ?
SELECT countrycode, `values`,
#curRank := #curRank + 1 AS rank
FROM gainfinal CROSS JOIN
(SELECT #curRank := 0) vars
WHERE year = 2000
ORDER By `values` DESC
LIMIT 10;
Use
LIMIT
in SQL so example:
SELECT countrycode, `values`,
#curRank := #curRank + 1 AS rank
FROM gainfinal CROSS JOIN
(SELECT #curRank := 0) vars
WHERE year = 2000
ORDER By `values` DESC
LIMIT 10
you can get 10 rows starting from row 20 using:Limit
LIMIT 10 OFFSET 20 --Equivalent to LIMIT 20, 10
You can also use your rank variable in where statement
...WHERE year = 2000 and #curRank <11;
I have a 1 table database with (in a simplified form) the following fields:
user_id(INT), ref_id(INT), points(INT), pointsgiven(BOOL/TINY_INT)
I want a query that returns the RANK of the user_id I specify based on points, given that pointsgiven is true. The kicker is, I need ties included. I can get a result set for ALL ranks if I want with the following query
SELECT
user_id, ref_id, points, pointsgiven,
CASE
WHEN #points = COALESCE(points, 0) THEN #rownum
ELSE #rownum := #rownum + 1
END AS rank,
#points := COALESCE(points, 0)
FROM users CT
JOIN
(
SELECT #rownum := 0, #points := NULL
) r
WHERE pointsgiven=TRUE ORDER BY points DESC
So, based on that, I thought I could just use that as a subquery to get a certain user_id as follows:
select * from
(
SELECT
user_id, ref_id, points, pointsgiven,
CASE
WHEN #points = COALESCE(points, 0) THEN #rownum
ELSE #rownum := #rownum + 1
END AS rank,
#points := COALESCE(points, 0)
FROM users CT
JOIN
(
SELECT #rownum := 0, #points := NULL
) r
WHERE pointsgiven=TRUE ORDER BY points DESC
) as derived WHERE user_id = 15
But this returns [BLOB - 1 B] as the rank on the correct user_id. What am I doing wrong here?
I have no idea why your query isn't working. For a single user id, though, you can use a correlated subquery:
select user_id, ref_id, points, pointsgiven,
coalesce((select count(distinct user_id)
from users u2
where u2.pointsgiven=TRUE and
u2.points > u.points
) + 1, 1) as rank
from users u
where user_id = 15;
An index on users(pointsgiven, points, user_id) should be used by the query.
To look at just one ranking, this might even be faster than your method.
I'm trying to modify the following query to find the rank of a specific videoid and I'm not having much luck can anyone suggest a solution?
SELECT videoid wins/loses as win_loss,
#curRank := #curRank + 1 AS rank
FROM cb_video,
(SELECT #curRank := 0) r
ORDER BY wins/loses DESC
I tried doing a subquery like this but it fails:
SELECT rank
FROM (SELECT videoid wins/loses as win_loss,
#curRank := #curRank + 1 AS rank
FROM cb_video,
(SELECT #curRank := 0) r
ORDER BY wins/loses DESC)
WHERE videoid = 116
Also adding the videoid to the WHERE clause without a subquery just always shows the rank being the #1 position as it only returns one row:
SELECT videoid wins/loses as win_loss,
#curRank := #curRank + 1 AS rank
FROM cb_video,
(SELECT #curRank := 0) r
WHERE videoid = 116
ORDER BY wins/loses DESC
Any ideas how to limit the result to a specific ID but still retain the rank? FYI I keep two columns (wins and loses) if that helps.
SELECT a.videoid,
(SELECT COUNT(*) FROM cb_video b
WHERE a.videoid !=b.videoid
AND (b.wins/b.loses) > (a.wins/a.loses))+1 AS rank
FROM cb_video a
WHERE a.videoid = 116
Try something like this:
SELECT videoid, rank FROM (SELECT videoid, wins/loses as win_loss, #curRank := #curRank + 1 AS rank FROM cb_video, (SELECT #curRank := 0) r ORDER BY wins/loses DESC) s WHERE videoid = 116
I've tested this on simple subset created of similar table as you've described...
It returns the ONE video and its actual final Rank of the entire set...
select *
from ( SELECT
videoid,
wins,
losses,
wins/losses,
#curRank := #curRank +1 Rank
FROM
cb_video,
( select #curRank := 0 ) r
order by
wins/losses desc ) PreQuery
where
PreQuery.VideoID = 116