2012-08-06 107 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