2012-08-06 68 views
4

这是原始数据,并且希望根据分数(count(tbl_1.id))对它们进行排名。MySQL + PHP:优化排名查询并计数子查询

[tbl_1] 
=========== 
id | name 
=========== 
1 | peter 
2 | jane 
1 | peter 
2 | jane 
3 | harry 
3 | harry 
3 | harry 
3 | harry 
4 | ron 

因此,让临时表(tbl_2)计算每个id的分数。

SELECT id, name, COUNT(id) AS score 
FROM tbl_1 
GROUP BY id 
ORDER BY score DESC; 
LIMIT 0, 30; 

然后结果是;

[tbl_2] 
=================== 
id | name | score 
=================== 
3 | harry | 4 
1 | peter | 2 
2 | jane | 2 
4 | ron | 1 

然后查询这个;

SELECT v1.id, v1.name, v1.score, COUNT(v2.score) AS rank 
FROM votes v1 
JOIN votes v2 ON v1.score < v2.score 
OR (
v1.score = v2.score 
AND v1.id = v2.id 
) 
GROUP BY v1.id, v1.score 
ORDER BY v1.rank ASC, v1.id ASC 
LIMIT 0, 30; 

然后结果是;

========================== 
id | name | score | rank 
========================== 
3 | harry | 4  | 1 
1 | peter | 2  | 2 
2 | jane | 2  | 2 
4 | ron | 1  | 4 

是否可以在一个事务(查询)中很好地做到这一点?

回答

2

是的,可以在单个查询中执行此操作。但是这是MySQL中的一个总体毛球,因为MySQL没有简单的ROWNUM操作,并且您需要一个用于排名计算。

这是您的投票查询,并显示排名。 @ranka变量用于对行进行编号。

SELECT @ranka:[email protected]+1 AS rank, id, name, score 
    FROM 
    (
    SELECT id, 
      name, 
      COUNT(id) AS score 
     FROM tbl_1 
     GROUP BY id 
     ORDER BY score DESC, id 
    ) votes, 
    (SELECT @ranka:=0) r 

正如你已经发现的,你需要自己加入这个东西,以获得一个正确的排名(正确处理关系)。因此,如果您将查询替换为您的votes表中的两个引用,并使用它们自己的子查询版本,那么您将获得所需的内容。

SELECT v1.id, 
     v1.name, 
     v1.score, 
     COUNT(v2.score) AS rank 
    FROM (
     SELECT @ranka:[email protected]+1 AS rank, 
       id, 
       name, 
       score 
      FROM 
       (
       SELECT id, 
         name, 
         COUNT(id) AS score 
        FROM tbl_1 
       GROUP BY id 
       ORDER BY score DESC, name 
       ) votes, 
     (SELECT @ranka:=0) r) v1 
    JOIN (
     SELECT @rankb:[email protected]+1 AS rank, 
       id, 
       name, 
       score 
      FROM 
       (
       SELECT id, 
         name, 
         COUNT(id) AS score 
        FROM tbl_1 
        GROUP BY id 
        ORDER BY score DESC, name 
      ) votes, 
     (SELECT @rankb:=0) r) v2 
    ON (v1.score < v2.score) OR 
     (v1.score = v2.score AND v1.id = v2.id) 
GROUP BY v1.id, v1.score 
ORDER BY v1.rank ASC, v1.id ASC 
LIMIT 0, 30; 

告诉你这是一个毛球。请注意,在自行加入的子查询的两个版本中需要不同的@ranka和@rankb变量,以使行编号正确工作:这些变量在MySQL中具有连接范围,而不是子查询范围。

http://sqlfiddle.com/#!2/c5350/1/0显示此工作。

编辑:这是更容易做到这一点使用PostgreSQL的RANK()函数。

SELECT name, votes, rank() over (ORDER BY votes) 
    FROM (
     SELECT name, count(id) votes 
      FROM tab 
     GROUP BY name 
     )x 

http://sqlfiddle.com/#!1/94cca/18/0

+0

谢谢你的非常明确的解释和美妙的代码!我尝试了以前的代码(SELECT @a,@a:= @ a + 1 ...),但它没有在相同分数上返回正确的排名。非常感谢:) – chloe 2012-08-06 19:27:24

+0

第一部分代码是你完成这项工作所需要的一部分。这是子查询重复两次,代替你的“投票”表格,在你正确的查询中。如果有帮助,可以接受Stack Overflow的答案。点击绿色复选框。 – 2012-08-06 21:36:02