2016-11-28 227 views
0

我對MySQL相當陌生,並且一直在研究以下查詢以從wordpress users/usermeta表中提取值列表。查詢工作除了表現是可怕的,我不知道如何改進它。優化/ MySQL查詢

查詢需要12+秒才能完成,只有400個左右的用戶,如果添加更多的話,這將成爲一個巨大的問題。

我注意到MySQL報告沒有唯一的列,但我不知道如何解決這個問題,因爲用戶ID已經是結果的一部分了。

這是默認查詢,完整查詢使用緯度/長度值添加額外計算來確定郵政編碼搜索的距離,但這似乎不會增加太多額外時間。

我掙扎的主要部分是值被保存爲meta_key和meta_value與關聯的user_id作爲外鍵的事實。

SELECT DISTINCT 
    wp_users.ID, 
    wp_users.user_email, 
    city_latitude.meta_value as cityLat, 
    city_longitude.meta_value as cityLong, 
    service_name.meta_value as service_name, 
    service_address.meta_value as service_address, 
    service_category.meta_value as service_category, 
    service_level.meta_value as service_level, 
    service_info.meta_value as service_info, 
    service_area.meta_value as service_area, 
    service_active.meta_value as service_active, 
    service_keyword.meta_value as service_keyword 
FROM 
    wp_usermeta AS city_latitude 
    LEFT JOIN wp_usermeta as city_longitude ON city_latitude.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_name ON service_name.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_category ON service_category.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_address ON service_address.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_level ON service_level.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_info ON service_info.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_area ON service_area.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_active ON service_active.user_id = city_longitude.user_id 
    LEFT JOIN wp_usermeta as service_keyword ON service_keyword.user_id = city_longitude.user_id 
    INNER JOIN wp_users ON wp_users.ID = city_latitude.user_id 

WHERE city_latitude.meta_key = 'service_lat' 
    AND city_longitude.meta_key = 'service_long' 
    AND (service_name.meta_key = 'service_name' AND service_name.meta_value != 'Anonymous') 
    AND (service_category.meta_key = 'service_category' ".$query_category.") 
    AND service_address.meta_key = 'service_address' 
    AND (service_level.meta_key = 'wp_user_level' AND service_level.meta_value = 0) 
    AND service_info.meta_key = 'service_additional' 
    AND service_area.meta_key = 'service_area' 
    AND service_keyword.meta_key = 'service_keywords' 
    AND (service_active.meta_key = 'active' AND service_active.meta_value = 1) 
ORDER BY service_name ASC 

我已經更新了查詢,其中一些建議已經有了很大的改變。我仍然認爲查詢本身可以通過減少連接數量等來改善,如果有人有任何更多的想法我很樂意聽到他們。

這是查詢,其中包括用距離搜索的更改和附加部分。

SELECT DISTINCT 
    wp_users.ID, 
    wp_users.user_email, 
    city_latitude.meta_value as cityLat, 
    city_longitude.meta_value as cityLong, 
    service_name.meta_value as service_name, 
    service_address.meta_value as service_address, 
    service_category.meta_value as service_category, 
    service_level.meta_value as service_level, 
    service_info.meta_value as service_info, 
    service_area.meta_value as service_area, 
    service_active.meta_value as service_active, 
    service_keyword.meta_value as service_keyword, 
    ((ACOS(SIN($userLat * PI()/180) * SIN(city_latitude.meta_value * PI()/180) + COS($userLat * PI()/180) * COS(city_latitude.meta_value * PI()/180) * COS(($userLng - city_longitude.meta_value) * PI()/180)) * 180/PI()) * 60 * 1.1515) AS distance 
FROM 
    wp_usermeta AS usermeta 
    LEFT JOIN wp_usermeta as city_longitude ON city_longitude.user_id = usermeta.user_id AND city_longitude.meta_key = 'service_long' 
    LEFT JOIN wp_usermeta as city_latitude ON city_latitude.user_id = usermeta.user_id AND city_latitude.meta_key = 'service_lat' 
    LEFT JOIN wp_usermeta as service_name ON service_name.user_id = usermeta.user_id AND service_name.meta_key = 'service_name' 
    LEFT JOIN wp_usermeta as service_category ON service_category.user_id = usermeta.user_id AND service_category.meta_key = 'service_category' 
    LEFT JOIN wp_usermeta as service_address ON service_address.user_id = usermeta.user_id AND service_address.meta_key = 'service_address' 
    LEFT JOIN wp_usermeta as service_level ON service_level.user_id = usermeta.user_id AND service_level.meta_key = 'wp_user_level' 
    LEFT JOIN wp_usermeta as service_info ON service_info.user_id = usermeta.user_id AND service_info.meta_key = 'service_additional' 
    LEFT JOIN wp_usermeta as service_area ON service_area.user_id = usermeta.user_id AND service_area.meta_key = 'service_area' 
    LEFT JOIN wp_usermeta as service_active ON service_active.user_id = usermeta.user_id AND service_active.meta_key = 'active' 
    LEFT JOIN wp_usermeta as service_keyword ON service_keyword.user_id = usermeta.user_id AND service_keyword.meta_key = 'service_keywords' 
    INNER JOIN wp_users ON wp_users.ID = usermeta.user_id 
WHERE (service_name.meta_value != '' AND service_name.meta_value != 'Anonymous') AND service_level.meta_value = 0 AND service_active.meta_value = 1 
HAVING distance < $search_distance 
ORDER BY distance ASC 
+3

刪除這句話是什麼EXPLAIN給?請告訴我們這個。有沒有需要全表掃描的連接?而且您需要將WHERE條件添加到適當的ON部分! – Seb

+0

@P蒼鷺 - 你可以從表格中創建一些樣本日期。沒有必要加入wp_usermeta超過1次。如果您通過http:// sqlfiddle發送一些數據,我會爲您提供查詢。com/ –

+0

@Bernd Buffen - 那真是太好了,謝謝。我已經在[link](http://sqlfiddle.com/#99/af672a)設置了一些數據,我以前從來沒有使用過它,所以它很好。 –

回答

0

你可以試試這個。如果您向我發送更多數據,我可以更快地優化查詢 。您必須根據距離修改計算。我有一個const(2)更換PHP值測試,也在其行

SELECT 
    res.* 
    ,((ACOS(SIN(2 * PI()/180) * SIN(cityLat * PI()/180) 
    + COS(2 * PI()/180) * COS(cityLat * PI()/180) 
    * COS((2 - cityLong) * PI()/180)) * 180/PI()) * 60 * 1.1515) AS distance 
    FROM (
     SELECT 
      wpu.ID 
      , wpu.user_email 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_lat'   ,wpm.meta_value,'') SEPARATOR '') AS cityLat 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_long'   ,wpm.meta_value,'') SEPARATOR '') AS cityLong 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_name'   ,wpm.meta_value,'') SEPARATOR '') AS service_name 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_address'  ,wpm.meta_value,'') SEPARATOR '') AS service_address 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_category'  ,wpm.meta_value,'') SEPARATOR '') AS service_category 
      , GROUP_CONCAT(if(wpm.meta_key = 'wp_user_level'   ,wpm.meta_value,'') SEPARATOR '') AS service_level 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_additional' ,wpm.meta_value,'') SEPARATOR '') AS service_info 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_area'   ,wpm.meta_value,'') SEPARATOR '') AS service_area 
      , GROUP_CONCAT(if(wpm.meta_key = 'active'     ,wpm.meta_value,'') SEPARATOR '') AS service_active 
      , GROUP_CONCAT(if(wpm.meta_key = 'service_keywords'  ,wpm.meta_value,'') SEPARATOR '') AS service_keyword 
    FROM wp_users AS wpu 
    LEFT JOIN wp_usermeta as wpm ON wpm.user_id = wpu.id  
    GROUP BY wpm.user_id 
) as res 
WHERE 
    (res.service_name != '' AND res.service_name != 'Anonymous') 
    AND res.service_level = 0 
    AND res.service_active = 1 
-- HAVING distance < $search_distance 
ORDER BY distance ASC; 

結果

ID user_email meta_key cityLat cityLong service_name service_address service_category service_level service_info service_area service_active service_keyword distance 
2 [email protected] wp_user_level 54.90131 -1.385126 service2 7 address1 address2 Support Info and Advice|Health and Wellbeing|Things to do|Education Training|Volunteering 0 A strong, local, independent charity working with and for older people (those 50+) Sunderland 1  3659.9253142487732 
3 [email protected] wp_user_level 54.897923 -1.514989 service 3 6 address1 address2 Support Info and Advice|Children Young people and Families|Health and Wellbeing|Things to do|Housing and your home|Education Training|Employment|Volunteering|Money Matters|Mental Health|Cultural 0 is a local independent charity offering a range of mental health and wellbeing services and training for the local community throughout . Sunderland 1  3660.080614342941 
+0

非常感謝,我會試一試,讓你知道它如何去 –

+0

它的工作甚至更好,然後我預期,查詢現在下降到0.09秒左右。再次感謝您的幫助。 –

0

我認爲你的查詢是相當大的。在wordpress中,我不確定你是否可以將它分解成更小的部分,然後結合其結果。

如果你想保留這個查詢,我建議1)你檢查你正在做的必要的列加入索引2)where子句中必要的列被索引。

必要的列:您實際上可以在連接中的所有列上放置索引,但是它會降低插入速度。 所以你可以嘗試把索引放在屬於表的巨大的列上。

1

您需要直接在ON部分添加where條件。否則,你會首先加入任何東西(這是巨大的),然後減少它。所以你可以在加入時直接減少它。應該快得多。

LEFT JOIN wp_usermeta as city_longitude ON city_latitude.user_id = city_longitude.user_id AND city_longitude.meta_key = 'service_long' 

+0

我不認爲這在MySQL中是正確的。連接中的條件與WHERE條件中的條件相同。真正的是,在'WHERE'子句中添加一個條件會隱式地將'LEFT JOIN'變成一個'INNER JOIN',但我認爲OP的問題可能是正確的。 –