2013-02-23 63 views
0

我有一個數據庫用戶facebook_accounts,屬於用戶。用戶按照與其他用戶相關的點數「排列」:這是通過嵌入式SELECT語句完成的,該語句用比用戶多得多的點數來計算所有用戶。MySQL排名查詢和左加入

該數據庫擁有〜10k個用戶。下面的SQL查詢需要的MySQL〜0.16s滿足:

SELECT 
    *, (SELECT (COUNT(*) + 1) 
      FROM users AS UserHigher 
      WHERE UserHigher.points > User.points 
     ) AS rank 
FROM 
    users AS User 
ORDER BY 
    User.points DESC, User.created ASC 
LIMIT 0, 30 

然而,加入LEFT JOIN也檢索用戶的facebook_account掛起MySQL的:

SELECT 
    *, (SELECT (COUNT(*) + 1) 
      FROM users AS UserHigher 
      WHERE UserHigher.points > User.points 
     ) AS rank 
FROM 
    users AS User 
LEFT JOIN 
    facebook_accounts AS FacebookAccount 
     ON (FacebookAccount.user_id = User.id) 
ORDER BY 
    User.points DESC, User.created ASC 
LIMIT 0, 30 

據我所知,COUNT()選擇排序用戶的方法效率不高,但這是我遇到的最可靠的方法。我不明白的是,爲什麼一個簡單的LEFT JOIN會破壞一個合理的查詢,當它看起來與排名SELECT語句完全分離時。

有什麼建議嗎?

+0

你對這些列的索引? 'EXPLAIN'告訴你什麼? – 2013-02-23 00:24:41

+0

不要每次都讀取所有數據。 – symcbean 2013-02-23 00:44:04

+0

所有表都有唯一的每行數字索引,而FacebookAccounts中的user_id是foriegn鍵列。 這是兩個查詢的EXPLAIN的屏幕截圖。 https://dl.dropbox.com/u/225179/temp/sql-queries.png 看起來JOIN強制PRIMARY用戶表使用「臨時」,所以也許從限制0,30的優化在那裏丟失 – 2013-02-23 00:46:06

回答

1

我的猜測是原始查詢首先執行排序,並且只執行排名30次。第二個查詢太複雜了,MySQL無法檢測到這種優化。

下可能會有所幫助:

select * 
from (SELECT *, (SELECT (COUNT(*) + 1) 
       FROM users AS UserHigher 
       WHERE UserHigher.points > User.points 
       ) AS rank 
     FROM users AS User 
     ORDER BY User.points DESC, User.created ASC 
     LIMIT 0, 30 
    ) t join 
    facebook_accounts AS FacebookAccount 
    ON (FacebookAccount.user_id = User.id) 
order by points desc, created asc 
+0

你必須是正確的,左連接必須破壞LIMIT提供的優化。你的查詢也解決了這個問題 - 謝謝! – 2013-02-23 00:50:02