views:

55

answers:

2

I need some help with a MySQL query. I'm trying to rank participants using a WHERE clause. So, I'd like to rank novices, intermediates, and experienced separately. For example:

Rank Name    Grade  Type
----------------------------------
1    Bob     98     Novice 
2    Jill    88     Novice 
3    Jimmy   42     Novice 
1    Mark    87     Intermediate 
2    Scott   85     Intermediate
3    Jim     77     Intermediate 
1    Jane    90     Advanced 
2    John    89     Advanced 
3    Josh    87     Advanced

I've tried:

SET @rank=0;

(SELECT @rank:=@rank+1 AS rank, name, grade, type FROM myTable WHERE type='novice' ORDER BY grade DESC)
UNION ALL
(SELECT @rank:=@rank+1 AS rank, name, grade, type FROM myTable WHERE type='intermediate' ORDER BY grade DESC)
UNION ALL
(SELECT @rank:=@rank+1 AS rank, name, grade, type FROM myTable WHERE type='experienced' ORDER BY grade DESC)

I guess that I need to re-set the rank somehow. Maybe I have another problem?

+1  A: 

Use:

SELECT t.name,
       t.grade,
       t.type,
       (SELECT COUNT(*)
          FROM YOUR_TABLE x
         WHERE x.type = t.type
           AND x.grade >= t.grade) AS rank
 FROM YOUR_TABLE t

To handle two grade columns, use:

SELECT t.name,
       t.grade1,
       t.grade2,
       t.type,
       (SELECT COUNT(*)
          FROM YOUR_TABLE x
         WHERE x.type = t.type
           AND (x.grade1 + x.grade2) >= (t.grade1 + t.grade2)) AS rank
 FROM YOUR_TABLE t

How would I rank the novice and intermediates together and the advanced separately?

Use:

SELECT y.*,
       (SELECT COUNT(*)
          FROM (SELECT *, 
                       CASE t.type
                         WHEN 'Advanced' THEN t.type
                         ELSE 'Non-Advanced'
                       END AS group_type
                  FROM YOUR_TABLE) x
         WHERE x.group_type = y.group_type
           AND (x.grade1 + x.grade2) >= (y.grade1 + y.grade2)) AS rank
  FROM (SELECT t.name,
               t.grade1,
               t.grade2,
               t.type,
               CASE t.type
                 WHEN 'Advanced' THEN t.type
                 ELSE 'Non-Advanced'
               END AS group_type
          FROM YOUR_TABLE t) y
OMG Ponies
Doesn't handle ties correctly. Maybe you want COUNT(*) + 1 with a condition of x.grade > t.grade.
Justin K
OMG Ponies
I'm making the assumption that we use the highest rank possible for ties. So if two people have the same highest score, they both should get rank 1. (I agree that whether the next person gets 2 or 3 is subjective.)
Justin K
Hi OMGPonies, You're the MySQL OMGStallion. You always help me out. I'm getting a error message when I try the above. What is TABLE x? Do I need to make a separate table with the type? Is it a 'dummy table'? I'm getting:#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'TABLE x WHERE x.type = t.type AND x.grade >= t.grade) AS ran' at line 5Thanks!
Laxmidi
@Laxmidi: It's the same table as `t` is. I'll update the answer to make it more obvious. I'm thankful to be a stallion, and not a gelding ;)
OMG Ponies
Hi OMGPonies, Sweet it works. I should have understood that it was the same table. Thanks.
Laxmidi
Hi OMGPonies,Sorry I need a little more help. Imagine that I have a second grade column (grade2). I add columns grade and grade2 and use the alias combo. I'm trying to get it to rank based on combo instead of grade. I tried:SELECT t.name, t.grade, t.grade2, t.type, (t.grade + t.grade2) AS combo (SELECT COUNT(*) FROM tester x WHERE x.type = t.type AND x.combo >= t.combo) AS rank FROM tester tBut, I get an error. It doesn't like the WHERE clause where it reads combo. Any suggestions. Thanks!
Laxmidi
OMG Ponies
OMGPonies, Victory! It worked. Thank you so much.
Laxmidi
Hi OMGPonies,Sorry, I need help with one more tweak. This query turned out more complicated than I thought!How would I rank the novice and intermediates together and the advanced separately? Bob thru Jim would be ranked 1-6 and the advanced would be ranked 1-3. So I need to change x.type = t.type somehow. Any thoughts? Thank you.
Laxmidi
@Laxmidi: Updated again. Any more updates, and you need to make a new question...
OMG Ponies
Hi OMGPonies, Okay. Promise no more updates on this one.
Laxmidi
A: 

You can do it one go, by using a more complicated ORDER BY clause.

SELECT @rank:=@rank+1 AS rank, name, grade, type 
FROM myTable 
ORDER BY 
    case type when 'novice'       then 1
              when 'intermediate' then 2 
              when 'experienced'  then 3 end,
    grade DESC
;

To make it even better, you could use a Type table to store the sort order and then add a join and then simply order by the type.sort_order then the grade, eg.

SELECT @rank:=@rank+1 AS rank, name, grade, type 
FROM myTable 
JOIN Types ON myTable.type = Types.type 
ORDER BY Types.sort_order, grade DESC;
ar
Hi ar,Thank you for your post! I tried the first option that you listed. Unfortunately, I get a rank of 1-9 (instead of 1-3 for each type). It returned:<code>rank name grade type1 Jane 90 Advanced2 John 89 Advanced3 Josh 87 Advanced4 Bob 98 Novice5 Jill 88 Novice6 Jimmy 42 Novice7 Mark 87 Intermediate8 Scott 85 Intermediate9 Jim 77 Intermediate</code>I'm trying to make a separate rank for each type. Any suggestions? Thanks!
Laxmidi
Hi ar, Sorry, I had what mySQL returned as a little table, but it lost the formatting when I put it in the comment.
Laxmidi