2012-02-16 90 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