2010-05-07 75 views
1

我需要優化一個查詢的排名是永久的(查詢本身的作品,但我知道這是可怕的,我剛剛試了很多記錄,它會給超時) 。優化慢速排名查詢​​

我會簡單介紹一下這個模型。我有3個表格:球員,球隊和player_team。我有球員,可以屬於一個球隊。聽起來很明顯,球員被存儲在球員表中並且團隊合作。在我的應用程序中,每個玩家可以隨時切換隊伍,並且必須記錄日誌。然而,在給定時間,球員被認爲只屬於一個球隊。現在的球員隊伍是他加入的最後一個球隊。

我認爲球員和球隊的結構並不相關。我有一個id列在每個PK。在player_team中,我有:

id   (PK) 
player_id (FK -> player.id) 
team_id  (FK -> team.id) 

現在,每個球隊都會爲每個參加球員的球員分配一個積分。所以,現在我想要得到最多球員人數最多的前N隊的排名。

我的第一個想法是首先從player_team中獲得當前球員(這是每個球員的最高記錄;該記錄必須是球員當前的球隊)。我沒有找到一個簡單的方法來做到這一點(嘗試GROUP BY player_team.player_id HAVING player_team.id = MAX(player_team.id),但這並沒有削減它

我試了一些querys didn'將不起作用,但設法得到這個工作。

SELECT 
    COUNT(*) AS total, 
    pt.team_id, 
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id) 
JOIN team t ON (t.id = pt.team_id) 
WHERE 
    pt.id IN (
     SELECT max(J.id) 
     FROM player_team J 
     GROUP BY J.player_id 
    ) 

GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50    

正如我所說的,它的工作原理,但看起來很糟糕,執行得很差,所以我敢肯定,必須有一個更好的方式去。任何人有任何想法優化呢?

我使用MySQL,順便說一句。

在此先感謝

添加說明。 (對不起,如何正確格式化)

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 PRIMARY  t ALL  PRIMARY  NULL NULL NULL 5000 Using temporary; Using filesort 
1 PRIMARY  pt ref  FKplayer_pt77082,FKplayer_pt265938,new_index FKplayer_pt77082 4 t.id 30 Using where 
1 PRIMARY  p eq_ref PRIMARY  PRIMARY  4 pt.player_id 1 
2 DEPENDENT SUBQUERY J index NULL new_index 8 NULL 150000 Using index 
+2

你永久留下曾經在player_team發生的每一個球員的團隊組合?你是不是以任何方式標記這一點(一個歷史關係爲0的列,當前一個很好的情況下爲1)? – marr75 2010-05-07 13:25:51

+0

是的,我要離開組合,因爲我必須保留一個日誌。我想過有一面旗幟,如果沒有更好的選擇,可能會這樣。但我也許有更好的辦法。 (我是一個sql noob!)感謝您的建議,但。 – 2010-05-07 13:31:53

+0

請發表您的解釋。 – 2010-05-07 13:36:25

回答

2

試試這個:

SELECT t.*, cnt 
FROM (
     SELECT team_id, COUNT(*) AS cnt 
     FROM (
       SELECT player_id, MAX(id) AS mid 
       FROM player_team 
       GROUP BY 
         player_id 
       ) q 
     JOIN player_team pt 
     ON  pt.id = q.mid 
     GROUP BY 
       team_id 
     ) q2 
JOIN team t 
ON  t.id = q2.team_id 
ORDER BY 
     cnt DESC 
LIMIT 50 

創建於player_team (player_id, id)這個工作速度快的指數(按照這個順序)。

+0

感謝Quassnoi。我認爲你的意思是在條件下pt.id = q.mid;改變這一點,並工作。我嘗試過,結果非常快。沒有檢查結果是否正確,但會盡快完成。再次感謝! – 2010-05-07 13:56:50

+1

+1記憶索引 – 2010-05-07 14:03:10

+0

對不起,我的意思是這個第二個ON條件,它應該是「t.id = q2.team_id」而不是「t.team_id = q2.team_id」 – 2010-05-07 15:01:11

1

我有時會發現MySQL中更復雜的查詢需要分解爲兩部分。

第一部分會將所需的數據提取到臨時表中,第二部分將是試圖操作創建的數據集的查詢。這樣做肯定會帶來顯着的性能提升。

+0

謝謝。這是我想到的第一個想法之一(但是有一個實際的表格)。我正在考慮的另一個選項是有一個標誌,將player_team關係標記爲當前/活動。 – 2010-05-07 13:34:23

2

它的子查詢正在殺死它 - 如果您在player_team表中添加current字段,那麼您給它的值爲1(如果它是最新的),如果它是舊的,則爲0您可以通過執行以下操作來簡化此操作:

SELECT 
    COUNT(*) AS total, 
    pt.team_id, 
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id) 
JOIN team t ON (t.id = pt.team_id) 
WHERE 
    player_team.current = 1 
GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50 

具有相同的關係在player_team表中的多個條目,其中區分哪一個是「當前」記錄的唯一方法是通過比較兩個(或更多)行,我認爲是不好的做法。我之前一直處於這種狀態,爲了使其工作真正殺死性能,必須採取一些變通辦法。通過簡單查找(在本例中爲where current=1)或通過將歷史數據移動到完全不同的表格(取決於您的情況,這可能是矯枉過正),能夠查看哪一行是最新的。

+0

謝謝。我正在考慮添加該列。只是想看看是否有其他選擇。 – 2010-05-07 13:36:53

+0

隨着當前標誌,你可以添加兩列,activate_datetime和inactivate_datetime這樣你就會知道,當實際的轉變是發生了。 – 2010-05-07 13:49:48

+0

@Nitin Midha。感謝您的建議。實際上我有一個「已創建」列來存儲插入行的時間戳(這是玩家加入團隊的時間)。我只是試圖在文章中留下不那麼重要的東西,而不是添加太多混亂。 – 2010-05-07 14:05:40

0

這將讓目前的團隊由大小排序的顏色:

SELECT team_id, COUNT(player_id) c AS total, t.color 
    FROM player_team pt JOIN teams t ON t.team_id=pt.team_id 
    GROUP BY pt.team_id WHERE current=1 
    ORDER BY pt.c DESC 
    LIMIT 50; 

但是你卻沒有給出哪個球員應該被認爲是球隊的老闆的條件。您當前的查詢是由於分組而將任何玩家任意顯示爲owner_id,而不是因爲該玩家是實際所有者。如果您的player_team表包含「所有者」列,則可以將上述查詢加入到所有者查詢中。喜歡的東西:

SELECT o.facebook_uid, a.team_id, a.color, a.c 
FROM player_teams pt1 
    JOIN players o ON (pt1.player_id=o.player_id AND o.owner=1) 
    JOIN (...above query...) a 
    ON a.team_id=pt1.team_id; 
0

你可以列「last_playteam_id」添加到播放表,每一個球員改變了他的團隊與來自player_team表的PK時間更新。

然後,你可以這樣做:

SELECT 
    COUNT(*) AS total, 
    pt.team_id, 
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id) and p.last_playteam_id = pt.id 
JOIN team t ON (t.id = pt.team_id) 
GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50 

這可能是最快的,因爲你沒有老player_team行更新到當前= 0。

您還可以添加,而不是列「last_team_id」,並保持它的當前團隊那裏,你得到最快的結果對於上面的查詢,但它可能是與其他查詢幫助較小。