2012-02-16 4 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 테이블 :

service_calls 테이블

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  
:

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  

(포맷에 대한 사과, 나는 최고의 한 내가 할 수있는)

더 많은 정보를 제공해 주시면 감사하겠습니다.

또한 정보 (I가 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

+0

[코드 검토] (http://codereview.stackexchange.com/?as=1) 또는 [데이터베이스 관리자] ] (http://dba.stackexchange.com/?as=1) –

답변

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 서버 버전에 해당하는 설명서를 확인하십시오. 'SELECT PK FROM service_calls AS scm WHERE ci.wf_obj = scm.wf_obj'줄 11에서 – davidjwest

+0

'PK'라고 쓰지 마십시오. 기본 키 열을 대신 입력하십시오. –

+0

많은 감사합니다. 지금은 훨씬 빨라 보이며 올바르게 작동하게하십시오. 다시보고 할 것입니다. – davidjwest

2

나는이 볼 줄 첫 번째 장소는 인덱스 될 것이다.

PK 인 ci.id에 그룹이 있지만 link_ser (소스 테이블이 지정되지 않음)로 주문하고 ci.acc_id를 기반으로 선택합니다.

필드 acc_id에 대해 corp_installs 테이블에 여분의 키를 추가하면 WHERE 절에 사용할 수 있으므로 성능 만 향상됩니다.

더 이상 찾으려면 ci.wf_obj = sc.wf_obj가 있어야합니다. 포함 된 VARCHAR에 가입하는 SLOW 될 것이며, 당신의 친구를 실제로 선택 기준의 일부로이 사용하지 않는 등 하위 쿼리가 될 수는이 외에도

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) 키가 될 때까지는 정렬에서 사용할 수 있습니다. 또한 serice_calls에 (wf_obj)에 키를 추가하십시오.

이것은 corpoprate_installs 테이블에서 20 개 행을 선택하고 만 비효율적 인 VARCHAR가

+0

감사합니다. 지금은 2 분의 1 초 더 빠르지 만 조금 더 나은 결과를 기대했습니다. 더 이상의 조언을 부탁드립니다. – davidjwest

+0

스키마를 살펴보면 ci.wf_obj = sc.wf_obj에 LEFT JOIN이 표시되며, 둘 다 VARCHAR (50)입니다. 특히 더 큰 데이터 세트에서는 느려질 것입니다. Wf_obj에 대한 키를 service_calls 테이블에 추가하고 이전 키를 acc_id, wf_obj로 변경하는 것도 약간의 변경 사항입니다. 그러나 이것은 크게 개선되지 않을 것입니다. 곧바로 답변을 업데이트 할 예정입니다. –

1

가 나는 함께 사용되는 SQL_CALC_FOUND_ROWS 옵션을 생각 나는이 도움이 희망

에 가입하여 service_calls 테이블에 그들을 참여합니다 조인 및 그룹을 사용하면 성능이 저하 될 수 있습니다 (일부 테스트에서는 here, 정보는 SQL_CALC_FOUND_ROWS here). 이 경우 색인이 사용되지 않는 것 같습니다.

검색어를 LIMIT 다음에 COUNT()가있는 별도의 검색어 두 개로 바꾸어보세요.

+0

감사합니다. funnily 내가 여기에 글을 게시하기 전에이 기사를 읽었습니다. 데이터의 양은 제가 쿼리를 분할하는 것이 많은 일을하는 것을 의심하지만, 일단 시작하면 더 많은 데이터를 얻습니다. 조언을 감사하십시오. – davidjwest