[英]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.