簡體   English   中英

具有許多聯接的MySQL錯誤查詢性能

[英]Mysql Bad Query performance with many joins

我有一個查詢,它需要幾分鍾才能在小型數據集上執行,這是什么問題? 這是解釋輸出:

id  select_type table           type    possible_keys               key                 key_len   ref             rows  Extra

1   SIMPLE      BT_CALL_CLASS.. const   PRIMARY                     PRIMARY             4         const           1     Using index; Using temporary; Using filesort
1   SIMPLE      BT_FLAGS_FLAGS  const   PRIMARY                     PRIMARY             4         const           1     Using index
1   SIMPLE      BT_DIRECTORY... range   PRIMARY                     PRIMARY             4         NULL            308   Using where; Using index
1   SIMPLE      BT_CALL_FLAGS.. ref     PRIMARY,FKDF68A2ED9F150002  FKDF68A2ED9F150002  4         const           49    Using where; Using index
1   SIMPLE      BT_DEPARTMENT.. index   PRIMARY                     FK1F3A276188DDBD3   5         NULL            27    Using where; Using index; Using join buffer
1   SIMPLE      BT_USERS_USER.. index   PRIMARY                     FK6A68E086C27A155   5         NULL            233   Using where; Using index; Using join buffer
1   SIMPLE      BT_FCT_CALLS..  eq_ref  PRIMARY,FKDF68A2ED4F28..    PRIMARY             8         ...call_id      1     Using where

查詢

 desc SELECT DISTINCT 
      BT_FCT_CALLS_FCT_CALLS.start_time AS COL0
     ,BT_FCT_CALLS_FCT_CALLS.calling_number AS COL1
     ,BT_FCT_CALLS_FCT_CALLS.called_number AS COL2
     ,BT_FCT_CALLS_FCT_CALLS.response AS COL3

 FROM departments BT_DEPARTMENTS_DEPARTMENTS_3 LEFT OUTER JOIN 
 ( 
  directory_numbers BT_DIRECTORY_NUMBERS_DIRECTORY_NUMBERS LEFT OUTER JOIN 
  ( 
   users BT_USERS_USERS_2 JOIN 
   ( 
    call_classification_dim BT_CALL_CLASSIFICATION_DIM_CALL_CLASSIFICATION_DIM JOIN 
    ( 
     flags BT_FLAGS_FLAGS JOIN 
     ( 
      fct_calls BT_FCT_CALLS_FCT_CALLS JOIN call_flags BT_CALL_FLAGS_CALL_FLAGS
      ON ( BT_FCT_CALLS_FCT_CALLS.id = BT_CALL_FLAGS_CALL_FLAGS.call_id )
     ) 
     ON ( BT_CALL_FLAGS_CALL_FLAGS.flag = BT_FLAGS_FLAGS.id AND (  BT_FLAGS_FLAGS.id  = 1 )  )
    ) 
    ON ( BT_FCT_CALLS_FCT_CALLS.call_direction_id = BT_CALL_CLASSIFICATION_DIM_CALL_CLASSIFICATION_DIM.id AND (  BT_CALL_CLASSIFICATION_DIM_CALL_CLASSIFICATION_DIM.id  = 2 )  )
   ) 
   ON ( (( BT_FCT_CALLS_FCT_CALLS.on_network_called_user_id  =  BT_USERS_USERS_2.id ) OR ( BT_FCT_CALLS_FCT_CALLS.on_network_calling_user_id  =  BT_USERS_USERS_2.id )) AND TRUE   )
  ) 
  ON ( (( BT_FCT_CALLS_FCT_CALLS.on_network_called_ext_id  =  BT_DIRECTORY_NUMBERS_DIRECTORY_NUMBERS.id ) OR ( BT_FCT_CALLS_FCT_CALLS.on_network_calling_ext_id  =  BT_DIRECTORY_NUMBERS_DIRECTORY_NUMBERS.id )) AND TRUE )
 ) 
 ON ( (( BT_FCT_CALLS_FCT_CALLS.on_network_called_department_id  =  BT_DEPARTMENTS_DEPARTMENTS_3.id ) OR ( BT_FCT_CALLS_FCT_CALLS.on_network_calling_department_id  =  BT_DEPARTMENTS_DEPARTMENTS_3.id )) AND TRUE )

  WHERE 
    (
      (
          NOT( BT_DEPARTMENTS_DEPARTMENTS_3.id  IN ( 1 ) )
      )
  AND (
          NOT( BT_DIRECTORY_NUMBERS_DIRECTORY_NUMBERS.id  IN ( 1 ) )
      )

  AND (
          NOT( BT_USERS_USERS_2.id  IN ( 1 ) )
      )
    )
ORDER BY 
      COL0

首先,將您的WHERE子句更改為:

 WHERE 
 BT_DEPARTMENTS_DEPARTMENTS_3.id  <> 1 AND 
 BT_DIRECTORY_NUMBERS_DIRECTORY_NUMBERS.id  <> 1 AND 
 BT_USERS_USERS_2.id <> 1

比使用NOT IN(..)更快

刪除不需要的AND TRUE

首先,讓我們從寫得很差的查詢流程開始。 通過簡化直接關系與所有嵌套的關系,可以幫助您清除一些東西。 其次,在事物相關的樹上顯示關系。 你到處彈跳。 我還縮短了“別名”的可讀性。 最后,您不需要所有表,因為您無法從所有表中獲取值,這使許多事情變得無用。 無論如何,這是原始查詢的清理版本。 如您所見,更容易看到表A鏈接到B的鏈接到C鏈接到D的鏈接等。

SELECT DISTINCT 
      calls.start_time AS COL0,
      calls.calling_number AS COL1,
      calls.called_number AS COL2,
      calls.response AS COL3
   FROM 
      fct_calls calls 
         JOIN departments dpt3 
            ON calls.on_network_called_department_id = dpt3.id
            OR calls.on_network_calling_department_id = dpt3.id
         JOIN users u2 
            ON calls.on_network_called_user_id = u2.id
            OR calls.on_network_calling_user_id = u2.id
         JOIN directory_numbers dirNums 
            ON calls.on_network_called_ext_id = dirNums.id 
            OR calls.on_network_calling_ext_id = dirNums.id 
         JOIN call_classification_dim classDim 
            ON calls.call_direction_id = classDim.id 
            AND classDim.id = 2
         JOIN call_flags callFlags
            ON calls.id = callFlags.call_id
            JOIN flags f 
               ON callFlags.flag = f.id 
               AND f.id = 1
  WHERE
         dpt3.id <> 1
     AND dirNums.id <> 1
     AND  u2.id = 1
  ORDER BY 
     COL0

現在,這就是我要處理的內容。 同樣,甚至沒有使用很多東西,例如呼叫標志和目錄號碼。 從所有具有您感興趣的用戶ID的呼叫開始,通過呼叫或被叫職位。 我會在on表上的調用表上有一個索引(on_network_Called_user_id,on_network_calling_user_id)。 我假設其他表在其各自的主“ ID”列上都有索引。

SELECT DISTINCT 
      calls.start_time AS COL0,
      calls.calling_number AS COL1,
      calls.called_number AS COL2,
      calls.response AS COL3
   FROM 
      fct_calls calls 
         JOIN departments dpt3 
            ON calls.on_network_called_department_id = dpt3.id
            OR calls.on_network_calling_department_id = dpt3.id
         JOIN directory_numbers dirNums 
            ON calls.on_network_called_ext_id = dirNums.id 
            OR calls.on_network_calling_ext_id = dirNums.id 
  WHERE
     (    calls.on_network_called_user_id = 1
       OR calls.on_network_calling_user_id = 1 )
     AND dpt3.id <> 1
     AND dirNums.id <> 1
  ORDER BY 
     COL0

更進一步,由於考慮調用或被調用,通過執行UNION可能會獲得更好的性能,您可以嘗試以下操作。 因此,您正在查找正在執行呼叫的人員是用戶ID 1,被叫部門不是1,目錄擴展名不是1的任何呼叫。或者,正在呼叫用戶ID 1,但是忽略部門1和擴展名1。

我將在呼叫表上為該呼叫者分配兩個索引,以及呼叫源於何處(on_network_Called_user_id,on_network_calling_department_id,on_network_calling_ext_id)這與之相反...呼叫者以及呼叫的部門/分機是( on_network_calling_user_id,on_network_called_department_id,on_network_called_ext_id)

SELECT
      calls.start_time AS COL0,
      calls.calling_number AS COL1,
      calls.called_number AS COL2,
      calls.response AS COL3
   FROM 
      fct_calls calls 
         LEFT JOIN departments dpt3 
            OR calls.on_network_calling_department_id = dpt3.id
         LEFT JOIN directory_numbers dirNums 
            OR calls.on_network_calling_ext_id = dirNums.id 
   WHERE
          calls.on_network_called_user_id = 1
      AND calls.on_network_calling_department_id <> 1
      AND calls.on_network_calling_ext_id <> 1
   ORDER BY 
      COL0
UNION
SELECT
      calls.start_time AS COL0,
      calls.calling_number AS COL1,
      calls.called_number AS COL2,
      calls.response AS COL3
   FROM 
      fct_calls calls 
         LEFT JOIN departments dpt3 
            OR calls.on_network_called_department_id = dpt3.id
         LEFT JOIN directory_numbers dirNums 
            OR calls.on_network_called_ext_id = dirNums.id 
   WHERE
          calls.on_network_calling_user_id = 1
      AND calls.on_network_called_department_id <> 1
      AND calls.on_network_called_ext_id <> 1

正如我在評論中說的那樣,查詢是自動生成的,無論如何,升級到mysql 5.6.14之后,無論如何我都無法對其進行大量修改,並且查詢本身也保持不變,因此性能變得正常了(相比之下到具有類似復雜性的其他查詢)!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM