2012-02-16 128 views
0

我繼承了一些代碼,原作者不可聯繫,我非常感謝任何幫助,因爲我自己的MySQL知識不是很好。MySQL查詢的性能

我有以下查詢需要大約4秒鐘執行,所有表中只有大約20,000行數據組合,因此我懷疑查詢可能會更有效率,也許可以通過將其分成多個查詢,在這裏它是:

SELECT SQL_CALC_FOUND_ROWS ci.id AS id, ci.customer AS customer, ci.installer AS installer, ci.install_date AS install_date, ci.registration AS registration, ci.wf_obj AS wf_obj, ci.link_serial AS link_serial, ci.sim_serial AS sim_serial, sc.call_status AS call_status 
    FROM ap_servicedesk.corporate_installs AS ci 
    LEFT JOIN service_calls AS sc ON ci.wf_obj = sc.wf_obj 
    WHERE ci.acc_id = 3 
    GROUP BY ci.id 
    ORDER BY link_serial 
       asc 
    LIMIT 40, 20 

任何人都可以找到任何方式使這更高效,謝謝。

(一些值被設置爲變量,但運行在phpMyAdmin上述查詢需要〜4secs)

id列是主索引。

更多信息的要求:

corporate_installs表:

Field  Type Null Key Default Extra 

id    int(11) NO PRI NULL auto_increment 
customer  varchar(800) NO  NULL  
acc_id  varchar(11) NO  NULL  
installer  varchar(50) NO  NULL  
install_date varchar(50) NO  NULL  
address_name varchar(30) NO  NULL  
address_street varchar(40) NO  NULL  
address_city varchar(30) NO  NULL  
address_region varchar(30) NO  NULL  
address_post_code varchar(10) NO  NULL  
latitude   varchar(15) NO  NULL  
longitude   varchar(15) NO  NULL  
registration varchar(50) NO  NULL  
driver_name   varchar(50) NO  NULL  
vehicle_type varchar(50) NO  NULL  
make   varchar(50) NO  NULL  
model   varchar(50) NO  NULL  
vin     varchar(50) NO  NULL  
wf_obj   varchar(50) NO  NULL  
link_serial   varchar(50) NO  NULL  
sim_serial   varchar(50) NO  NULL  
tti_inv_no   varchar(50) NO  NULL  
pro_serial   varchar(50) NO  NULL  
eco_serial   varchar(50) NO  NULL  
eco_bluetooth varchar(50) NO  NULL  
warranty_expiry varchar(50) NO  NULL  
project_no   varchar(50) NO  NULL  
status   varchar(15) NO  NULL  

service_calls表:

Field   Type   Null Key Default Extra 
id     int(11)  NO  PRI NULL auto_increment 
acc_id   int(15)   NO  NULL  
ciid   int(11)   NO  NULL  
installer_job_no varchar(50) NO  NULL  
installer_inv_no varchar(50) NO  NULL  
engineer   varchar(50) NO  NULL  
request_date varchar(50) NO  NULL  
completion_date varchar(50) NO  NULL  
call_status   varchar(50) NO  NULL  
registration varchar(50) NO  NULL  
wf_obj   varchar(50) NO  NULL  
driver_name   varchar(50) NO  NULL  
driver_phone varchar(50) NO  NULL  
team_leader_name varchar(50) NO  NULL  
team_leader_phone varchar(50) NO  NULL  
servicing_address varchar(150) NO  NULL  
region   varchar(50) NO  NULL  
post_code   varchar(50) NO  NULL  
latitude   varchar(50) NO  NULL  
longitude   varchar(50) NO  NULL  
incident_no   varchar(50) NO  NULL  
service_type varchar(20) NO  NULL  
fault_description varchar(50) NO  NULL  
requested_action varchar(50) NO  NULL  
requested_replacemt varchar(100) NO  NULL  
fault_detected varchar(50) NO  NULL  
action_taken varchar(50) NO  NULL  
parts_used   varchar(50) NO  NULL  
new_link_serial varchar(50) NO  NULL  
new_sim_serial varchar(50) NO  NULL  

(道歉的格式,我也盡我所能)

讓我知道如果你需要更多的信息,謝謝。

進一步信息(我再次用EXPLAIN做了查詢):

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE ci ALL acc_id NULL NULL NULL 7227 Using where; Using temporary; Using filesort 
1 SIMPLE sc ALL NULL NULL NULL NULL 410 
+0

您是否也可以提供表結構作爲您的問題可能更多,所以在那裏而不是查詢 – Ryan 2012-02-16 11:27:09

+0

也許更好的[code review](http://codereview.stackexchange.com/?as=1)或[數據庫管理員](http://dba.stackexchange.com/?as=1) – 2012-02-16 11:29:26

回答

2

在兩列wf_obj列,link_serial列(您可能還需要acc_id上的索引)上添加索引。

那就試試這個版本:

SELECT ... 
FROM 
     (SELECT * 
     FROM ap_servicedesk.corporate_installs 
     WHERE acc_id = 3 
     ORDER BY link_serial ASC 
     LIMIT 60 
    ) AS ci 
    LEFT JOIN service_calls AS sc 
    ON sc.PK =       --- the PRIMARY KEY of the table 
    (SELECT PK 
     FROM service_calls AS scm 
     WHERE ci.wf_obj = scm.wf_obj 
     ORDER BY scm. --- whatever suits you 
     LIMIT 1 
    ) 
ORDER BY ci.link_serial ASC 
LIMIT 20 OFFSET 40 

ORDER BY scm.SomeColumn需要不是性能而是爲了獲得一致的結果。您的查詢按原樣將第一個表中的行連接到第二個表的所有相關行。但是最後的GROUP BY聚集了所有這些行(第二個表),所以你的SELECT ... sc.call_status從這些行中選擇一個或多或少的隨機call_status

+0

謝謝,我收到一個錯誤:#1064 - 你的SQL語法錯誤;請檢查與您的MySQL服務器版本相對應的手冊,以便在第11行的'SELECT PK FROM service_calls AS scm WHERE ci.wf_obj = scm.wf_obj'附近使用正確的語法。 – davidjwest 2012-02-16 12:21:23

+0

不要寫'PK'。將主鍵列放在那裏。 – 2012-02-16 12:22:31

+0

非常感謝,現在看起來更快,讓我得到它正常工作,將報告回來。 – davidjwest 2012-02-16 12:26:45

2

我想看看在這的第一個地方將不得不索引。

在ci.id上有一個組,它是很好的PK,但是您正在通過link_ser(源表未指定)進行排序,並且您正在根據ci.acc_id進行選擇。

如果您在表corp_installs上爲字段acc_id添加一個額外的密鑰,那麼這樣做應該有助於提高性能,因爲它可以用於WHERE子句。

進一步看,您在連接中有ci.wf_obj = sc.wf_obj。加入一個VARCHAR將是緩慢的,而你實際上並沒有使用此作爲選擇標準的一部分,因此子查詢也可以是你的朋友,可以考慮以下

SELECT 
    serviceCallData.*, 
    sc.call_status AS call_status 

FROM (
    SELECT 
    SQL_CALC_FOUND_ROWS AS found_rows, 
    ci.id AS id, 
    ci.customer AS customer, 
    ci.installer AS installer, 
    ci.install_date AS install_date, 
    ci.registration AS registration, 
    ci.wf_obj AS wf_obj, 
    ci.link_serial AS link_serial, 
    ci.sim_serial AS sim_serial 

    FROM ap_servicedesk.corporate_installs AS ci 
    WHERE ci.acc_id = 3 
    GROUP BY ci.id 
    ORDER BY ci.link_serial ASC 
    LIMIT 40, 20 
) AS serviceCallData 
LEFT JOIN serice_calls AS sc ON serviceCallData.wf_obj = sc.wf_obj 

除此之外,改變(ACC_ID)關鍵是(acc_id,link_serial),然後它可以在排序中使用。還要將(wf_obj)上的密鑰添加到serice_calls中。

這將選擇從corpoprate_installs表中的20行,然後只使用低效VARCHAR加入

我希望這是幫助

+0

謝謝,現在比現在快了2秒,但我希望稍微好一點。任何更多的建議表示讚賞。 – davidjwest 2012-02-16 11:55:00

+0

看了看你的模式,最大的警鐘是你的左連接ci.wf_obj = sc.wf_obj,其中兩者都是VARCHAR(50)。特別是在較大的數據集上,這將會很慢。你可以做的另一個小改變是將一個wf_obj的關鍵字添加到service_calls表中,並將之前的關鍵字改爲acc_id,wf_obj,但是這可能不會有很大的改進。很快就會更新回答 – 2012-02-16 12:14:06

1

我想用用SQL_CALC_FOUND_ROWS選擇加入他們到service_calls表加入和一個組可能會降低性能(有些測試看here,有關SQL_CALC_FOUND_ROWS here的信息)。事實上,在這種情況下,索引不被使用。

嘗試用兩個單獨的查詢來替換您的查詢,即使用LIMIT後跟COUNT()。

+0

謝謝,有趣的是,我在閱讀這篇文章之前閱讀過這篇文章,並且與我一起工作的數據量很大,我懷疑分解查詢會做多少事情,但是一旦我們開始,我就會記住這一點獲取更多數據。欣賞建議。 – davidjwest 2012-02-16 12:49:25