2009-08-18 95 views
-1

This question問如何通過他的ID選擇用戶的排名。數據庫優化:計算排名

id  name  points 
1  john  4635 
3  tom  7364 
4  bob  234 
6  harry 9857 

接受的答案是

SELECT uo.*, 
     (
     SELECT COUNT(*) 
     FROM users ui 
     WHERE (ui.points, ui.id) >= (uo.points, uo.id) 
     ) AS rank 
FROM users uo 
WHERE id = @id 

這是有道理的。我想了解這種方法之間的性能折衷是什麼,或者通過修改數據庫結構來存儲計算出來的排名(我想這會在每次排名發生變化時都需要進行大規模的更改)或者其他任何方法我覺得太新奇了。我是一個db noob。

回答

1

的性能折衷基本上會是你所描述的:

如果您修改存儲等級結構,查詢將是非常,非常簡單和快速。然而,這將需要一些開銷隨時「點」改變,因爲你必須驗證排名沒有改變。如果排名發生了變化,您必須進行多次更新。

這會在每次更新/插入時導致更多的工作(可能存在錯誤)。權衡是非常快的讀取。如果您的典型用法與數百萬次讀取相比只有極少的修改,並且您發現此查詢是一個瓶頸,那麼可能需要考慮重新進行此操作。但是,除非您真的發現這是一個問題,否則我會避免增加的複雜性和可維護性問題,因爲當前的解決方案需要較少的存儲空間並且非常靈活。

0

該查詢的'where'部分在內部不需要讀取整個表嗎?我瞭解過早優化。在學術上,似乎這不會比幾千行進一步擴大。

1

您引用的鏈接是MySQL問題。如果原始數據庫是Oracle,則接受的答案是使用分析函數,該函數確實可以縮放:

SQL> select id, name, points from users order by id 
    2/

     ID NAME   POINTS 
---------- ---------- ---------- 
     1 john    4635 
     3 tom    7364 
     4 bob    234 
     6 harry   9857 
     8 algernon   1 
     9 sebastian   234 
     10 charles   888 

7 rows selected. 

SQL> select name, id, points, rank() over (order by points) 
    2 from users 
    3/

NAME    ID  POINTS RANK()OVER(ORDERBYPOINTS) 
---------- ---------- ---------- ------------------------- 
algernon   8   1       1 
bob     4  234       2 
sebastian   9  234       2 
charles   10  888       4 
john    1  4635       5 
tom     3  7364       6 
harry    6  9857       7 

7 rows selected. 

SQL> select name, id, points, dense_rank() over (order by points desc) 
    2 from users 
    3/

NAME    ID  POINTS DENSE_RANK()OVER(ORDERBYPOINTSDESC) 
---------- ---------- ---------- ----------------------------------- 
harry    6  9857         1 
tom     3  7364         2 
john    1  4635         3 
charles   10  888         4 
bob     4  234         5 
sebastian   9  234         5 
algernon   8   1         6 

7 rows selected. 

SQL>