2014-06-06 24 views
0

當我在phpMyAdmin中運行查詢時,它最多返回0.3s內的結果。通常0.19s ish是正常的。它似乎並沒有像查詢使用太多的CPU,但我的託管服務提供商告訴我,它消耗了很多。MySQL查詢消耗5-6倍正常CPU使用量

我在想,如果UNION ALL是這個問題的罪魁禍首,或者是一般查詢的複雜性。任何洞察力將不勝感激。

這裏的查詢:

SELECT unitId, unitName, createDate 
FROM (
    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate 
     FROM `game_player` 
     LEFT JOIN `game` ON game_player.gameId = game.id 
     LEFT JOIN `unit` ON game_player.unitId = unit.id 
     WHERE game_player.playerId = 123 
     AND game.createDate > 1390953600000 
     AND game_player.unitId NOT IN (SELECT unitId FROM unit_free)) 

    UNION ALL 

    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate 
     FROM `game_player` 
     LEFT JOIN `game` ON game_player.gameId = game.id 
     LEFT JOIN `unit` ON game_player.unitId = unit.id 
     WHERE game_player.playerId = 123 
     AND game.type = '5') 
) 
AS results 
WHERE unitId NOT IN (SELECT unitId FROM player_units WHERE playerId = 123) 
GROUP BY unitName 

game是遊戲列表
game_player是在一個特定的遊戲
unit的球員名單中的單位列表,玩家可以使用
unit_free是玩家可以玩的免費單位列表
player_units是玩家擁有的已知單位列表

回答

1

首先你的2個子查詢非常相似。它們可能都返回相同的行(因此所有的聯合),但是你使用GROUP_BY來消除重複之一。

您的第一個子查詢是在WHERE子句中檢查遊戲表(createDate)中的字段。爲了實現這一點,已經發現了很多匹配,因此可以使用INNER JOIN而不是LEFT OUTER JOIN。你的第二個子查詢對類型字段也是一樣的。

最後,您正在使用field IN (sub query)類型的語法,它在MySQL中進行了很少的優化。 LEFT OUTER JOIN然後檢查NULL可能會更快。

請注意,未定義返回unitName和createDate的最終unitName的值。可能是他們中的任何一個。

忽略這最後一點(如果你可以定義你想我可以嘗試進一步的方案,其值),則如下

SELECT unitId, unitName, createDate 
FROM (
    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate 
     FROM `game_player` 
     INNER JOIN `game` ON game_player.gameId = game.id AND game.createDate > 1390953600000 
     LEFT OUTER JOIN `unit` ON game_player.unitId = unit.id 
     LEFT OUTER JOIN unit_free ON game_player.unitId = unit_free.unitId 
     LEFT OUTER JOIN player_units ON game_player.unitId = player_units.unitId AND playerId = game_player.playerId 
     WHERE game_player.playerId = 123 
     AND unit_free.unitId IS NULL 
     AND player_units.unitId IS NULL) 

    UNION ALL 

    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate 
     FROM `game_player` 
     INNER JOIN `game` ON game_player.gameId = game.id AND game.type = '5' 
     LEFT OUTER JOIN `unit` ON game_player.unitId = unit.id 
     LEFT OUTER JOIN player_units ON game_player.unitId = player_units.unitId AND playerId = game_player.playerId 
     WHERE game_player.playerId = 123 
     AND player_units.unitId IS NULL 
     ) 
) 
AS results 
GROUP BY unitName 

如果game_player單元ID必須出現在單元表我我會調整查詢也會將這些更改爲內連接。

根據createDate的值的可能要求(即,如果多於一個返回,使用哪一個),那麼很可能將其減少爲單個查詢或一對聯合查詢和刪除任何子查詢的需要。

編輯

按照你的意見,這大概可以簡化爲: -

SELECT unit.id as unitId, unit.name as unitName, MAX(game.createDate) as createDate 
FROM `game_player` 
INNER JOIN `game` ON game_player.gameId = game.id 
INNER JOIN `unit` ON game_player.unitId = unit.id 
LEFT OUTER JOIN unit_free ON game_player.unitId = unit_free.unitId 
LEFT OUTER JOIN player_units ON game_player.unitId = player_units.unitId AND playerId = game_player.playerId 
WHERE game_player.playerId = 123 
AND ((unit_free.unitId IS NULL 
AND game.createDate > 1390953600000) 
OR AND game.type = '5') 
AND player_units.unitId IS NULL) 
GROUP BY unitName, unitId 

由於game_player.unitId總會出現在unit.id我們可以使用內部聯接,並依靠使用單元表中的id直接與單元名稱匹配 - 否則按單元名稱分組可能會導致兩個不同單元具有相同名稱時出現問題。由於您只需要一個創建日期,您可以使用MAX()獲取最新的日期。

儘管由於unit_free可能會返回多行,但group by將刪除這些行。由於你沒有總結或計算行數,這應該不重要。

假設適當的索引,這應該是相當快的。

+0

Ahhh,關於'IN'的提示很少優化,而使用'LEFT OUTER JOIN'而不是我正在尋找的東西。只要返回符合條件的所有單位中的一個條目,就返回「unitName」和「createDate」,這並不重要。最後,''game_player.unitId'會一直出現在'unit.id'中。 – Rawr

+0

哦!你能否包括CPU和不同操作的時間成本的分解,或者將我鏈接到具有此信息的站點。我想知道通過進行某些操作而犧牲了什麼CPU和時間。謝謝! – Rawr

+0

增加了一個建議。害怕我不能真正幫你打破時間,等等。 – Kickstart

0

請注意,您的內部子查詢的第一個是另一個子集的完美子集。考慮到這一點,爲什麼您首先需要UNION ALL?您的查詢應該是等同於本(除去除可能的重複):

SELECT unitId, unitName, createDate 
FROM (
    SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate 
    FROM `game_player` 
    LEFT JOIN `game` ON game_player.gameId = game.id 
    LEFT JOIN `unit` ON game_player.unitId = unit.id 
    WHERE game_player.playerId = 123 
) AS results 
WHERE unitId NOT IN (SELECT unitId FROM player_units WHERE playerId = 123) 
GROUP BY unitName 

然後,當你GROUP BY unitName,你應該使用在其他領域的一些聚合函數在你的外SELECT,像sum()count()avg()或類似 - 否則它根本就沒有意義。

解決這些問題後,運行包裝在EXPLAIN ANALYZE VERBOSE中的查詢,並確保查詢使用索引。如果它使用順序掃描或文件,確保添加缺少的索引,最有可能的是多列索引。

+0

啊你是對的,我修好了。第二個子查詢實際上具有第二個條件AND game.type ='5',所以第一個查詢收集玩家沒有使用空閒單位的遊戲,第二個查詢收集玩家玩遊戲類型「5」的遊戲。 – Rawr