2014-12-19 104 views
0

我想在大約30,000行的表(history_details)上運行一個查詢,需要連接幾個表(歷史記錄,房間,室友,屬性,listers,學生和管理員)。爲什麼我的MySQL查詢與多聯接如此緩慢?

  1. 第一次加入歷史表很簡單。
  2. 只有滿足條件時才應該添加下3個連接(房間,室友,屬性)。在這種情況下,它正在查看history_details表中的listing_type列的值是什麼,並且如果它是適當的值,則應該將該連接添加到查詢中。
  3. 之後的3個連接有幾個條件需要滿足才能包含它們,但在這些條件中可能是任何OR條件。

       SELECT SQL_CALC_FOUND_ROWS 
            history_details.history_id, 
            history_details.listing_type, 
            history_details.listing_id, 
            history_details.date_added, 
            history_details.field, 
            history_details.before_value, 
            history_details.after_value, 
            history_details.edit_group, 
            history_details.email, 
            history_details.name, 
            history_details.correction_message, 
            history_details.photo_filepath, 
            history.history_message, 
            listers.first_name, 
            listers.last_name, 
            students.first_name, 
            students.last_name, 
            admin.first_name, 
            admin.last_name 
           FROM history_details 
           LEFT JOIN history 
            ON history_details.history_id = history.history_id 
           LEFT JOIN rooms 
            ON history_details.listing_type="room" AND rooms.room_id = history_details.listing_id 
           LEFT JOIN roommates 
            ON history_details.listing_type="roommate" 
            AND roommates.roommate_id = history_details.listing_id 
           LEFT JOIN properties 
            ON history_details.listing_type="property" 
            AND properties.property_id = history_details.listing_id 
           LEFT JOIN listers 
            ON (history_details.listing_type="lister" AND listers.lister_id = history_details.listing_id) 
            OR (history_details.listing_type="property" AND listers.lister_id = properties.lister_id) 
            OR (history_details.listing_type="room" AND rooms.lister_type="lister" AND listers.lister_id = rooms.lister_id) 
           LEFT JOIN students 
            ON (history_details.listing_type="student" AND students.student_id = history_details.listing_id) 
            OR (history_details.listing_type="roommate" AND students.student_id = roommates.student_id) 
            OR (history_details.listing_type="room" AND rooms.lister_type="student" AND students.student_id = rooms.lister_id) 
           LEFT JOIN admin 
            ON history_details.listing_type="admin" AND admin.admin_id = history_details.listing_id 
           LIMIT 0,100 
    

它給我正確的結果,但它正在73秒運行。我添加了索引,但我不確定它們是否被使用。這是它顯示了當我運行使用EXPLAIN SELECT SQL_CALC_FOUND_ROWS ....

enter image description here

這是它顯示了當我運行使用EXPLAIN SELECT .... enter image description here

這是我加入的各種表的索引解釋說明: history_details enter image description here

PR operties enter image description here

客房 enter image description here

室友 enter image description here

你能告訴我,如果正在使用我的索引?另外,如何改進查詢,表格結構或索引以提高速度?

編輯: 感謝阿馬丹的有益建議,它現在運行在147ms而不是73秒之前。這是最終的查詢我最終使用:

   SELECT SQL_CALC_FOUND_ROWS 
        history_details.*, 
        listers.first_name AS lister_first_name, 
        listers.last_name AS lister_last_name, 
        NULL AS student_first_name, 
        NULL AS student_last_name, 
        NULL AS admin_first_name, 
        NULL AS admin_last_name 
       FROM history_details 
       LEFT JOIN history 
        ON history_details.history_id = history.history_id 
       LEFT JOIN listers 
        ON listers.lister_id = history_details.listing_id 
       WHERE history_details.listing_type="lister" 

       UNION 

       SELECT history_details.*, 
        NULL AS lister_first_name, 
        NULL AS lister_last_name, 
        students.first_name AS student_first_name, 
        students.last_name AS student_last_name, 
        NULL AS admin_first_name, 
        NULL AS admin_last_name 
       FROM history_details 
       LEFT JOIN history 
        ON history_details.history_id = history.history_id 
       LEFT JOIN students 
        ON students.student_id = history_details.listing_id 
       WHERE history_details.listing_type="student" 

       UNION 

       SELECT history_details.*, 
        listers.first_name AS lister_first_name, 
        listers.last_name AS lister_last_name, 
        NULL AS student_first_name, 
        NULL AS student_last_name, 
        NULL AS admin_first_name, 
        NULL AS admin_last_name 
       FROM history_details 
       LEFT JOIN history 
        ON history_details.history_id = history.history_id 
       LEFT JOIN properties 
        ON properties.property_id = history_details.listing_id 
       LEFT JOIN listers 
        ON listers.lister_id = properties.lister_id 
       WHERE history_details.listing_type="property" 

       UNION 

       SELECT history_details.*, 
        listers.first_name AS lister_first_name, 
        listers.last_name AS lister_last_name, 
        students.first_name AS student_first_name, 
        students.last_name AS student_last_name, 
        NULL AS admin_first_name, 
        NULL AS admin_last_name 
       FROM history_details 
       LEFT JOIN history 
        ON history_details.history_id = history.history_id 
       LEFT JOIN rooms 
        ON rooms.room_id = history_details.listing_id 
       LEFT JOIN listers 
        ON (rooms.lister_type="lister" AND listers.lister_id = rooms.lister_id) 
       LEFT JOIN students 
        ON (rooms.lister_type="student" AND students.student_id = rooms.lister_id) 
       WHERE history_details.listing_type="room" 

       UNION 

       SELECT history_details.*, 
        NULL AS lister_first_name, 
        NULL AS lister_last_name, 
        students.first_name AS student_first_name, 
        students.last_name AS student_last_name, 
        NULL AS admin_first_name, 
        NULL AS admin_last_name 
       FROM history_details 
       LEFT JOIN history 
        ON history_details.history_id = history.history_id 
       LEFT JOIN roommates 
        ON roommates.roommate_id = history_details.listing_id 
       LEFT JOIN students 
        ON students.student_id = roommates.student_id 
       WHERE history_details.listing_type="roommate" 

       UNION 

       SELECT history_details.*, 
        NULL AS lister_first_name, 
        NULL AS lister_last_name, 
        NULL AS student_first_name, 
        NULL AS student_last_name, 
        admin.first_name AS admin_first_name, 
        admin.last_name AS admin_last_name 
       FROM history_details 
       LEFT JOIN history 
        ON history_details.history_id = history.history_id 
       LEFT JOIN admin 
        ON admin.admin_id = history_details.listing_id 
       WHERE history_details.listing_type="admin" 
       ORDER BY history_details_id DESC 
       LIMIT 0,100 
+1

什麼'解釋select ....'顯示? – 2014-12-19 08:39:33

+0

你應該在每個案例中看到的第一件事是執行計劃。帶OR條件的 – 2014-12-19 08:41:05

+0

也可以減慢查詢速度。 – Abhijeet 2014-12-19 08:44:40

回答

1

其中keyNULL中的行,所以不能用這些表的任何索引。即listersstudents未被優化。那是因爲它的索引是不可能的,因爲你使用OR來產生它。做幾個查詢,其中每個history_details.listing_typeUNION他們。是的,它看起來很醜,這是很多重複,但速度會在那裏。

聯盟語法如下:

SELECT ... 
WHERE history_details.listing_type="lister" 
UNION 
SELECT ... 
WHERE history_details.listing_type="property" 
UNION 
SELECT ... 
WHERE history_details.listing_type="room" 
UNION 
SELECT ... 
WHERE history_details.listing_type="admin" 
.... 
... 
ORDER BY whatever 
LIMIT whatever, whatever 

確保每個SELECT具有字段(這樣你纔會有無用加入做就算了,但他們會很快,因爲它們會被索引的數量相等)。 ...基本上是您的整個查詢(SELECT field1, field2... FROM table1 LEFT JOIN table2 ON condition ...),但OR取出listerstudent條款。 UNION將組合所有子選擇的行。 ORDERLIMIT將適用於工會的結果,而不是最後的SELECT

+0

非常感謝您的建議。我不熟悉UNION。你有什麼樣的代碼?另外,如果我使用這種方法,我仍然會在一個結果集中收到所有返回的行嗎? – zeckdude 2014-12-19 08:54:32

+1

編輯了一個粗略的例子,真正的代碼太長了答案。如果'SELECT A'給你兩行,'1,2'和'SELECT B'給你'4,5,6',那麼'SELECT A UNION SELECT B'將給你5行。 – Amadan 2014-12-19 09:05:12

+0

好吧,我會給它一個鏡頭。謝謝!你是說我需要每個有條件的Select語句中的所有連接,或者只需要與這個特定條件有關的連接? – zeckdude 2014-12-19 09:21:28