2017-02-16 5 views
-2

를 사용하여이 쿼리 :느린 MySQL 쿼리, EXPLAIN shows Using temporary; filesort

EXPLAIN SELECT ppi_loan.customerID, 
       loan_number, 
       CONCAT(forename, ' ', surname) AS agent, 
       name, 
       broker, 
       (SELECT timestamp 
       FROM ppi_sar_status 
       WHERE history = 0 
        AND (status = 10 || status = 13) 
        AND ppi_sar_status.loanID = ppi_loan.loanID) AS ppi_unsure_date, 
       fosSent, 
       letterSent, 
       (SELECT timestamp 
       FROM ppi_ques_status 
       WHERE status = 1 
        AND ppi_ques_status.loanID = ppi_loan.loanID 
       ORDER BY timestamp DESC LIMIT 1) AS sent_date, 
       ppi_ques_status.timestamp 
FROM ppi_loan 
LEFT JOIN ppi_assignments ON ppi_assignments.customerID = ppi_loan.customerID 
LEFT JOIN italk.users ON italk.users.id = agentID 
LEFT JOIN ppi_ques_status ON ppi_ques_status.loanID = ppi_loan.loanID 
JOIN ppi_lenders ON ppi_lenders.id = ppi_loan.lender 
JOIN ppi_status ON ppi_status.customerID = ppi_loan.customerID 
JOIN ppi_statuses ON ppi_statuses.status = ppi_status.status 
    AND ppi_ques_status.status = 1 
    AND ppi_ques_status.history = 0 
    AND (cc_type = '' || (cc_type != '' AND cc_accepted = 'no')) 
    AND ppi_loan.deleted = 'no' 
    AND ppi_loan.customerID != 10 
GROUP BY ppi_loan.customerID, loan_number 

여기에, 매우 느립니다 쿼리 EXPLAIN의 모든 결과는 왜 그렇게 많은 행을 스캔하고 이유를 "일시적으로 사용; filesort 사용"

id select_type table type possible_keys key key_len ref rows Extra 
1 PRIMARY ppi_ques_status ref loanID,status,history status 3 const 91086 Using where; Using temporary; Using filesort 
1 PRIMARY ppi_loan eq_ref PRIMARY,customerID PRIMARY 8 ppimm.ppi_ques_status.loanID 1 Using where 
1 PRIMARY ppi_lenders eq_ref PRIMARY PRIMARY 4 ppimm.ppi_loan.lender 1 Using where 
1 PRIMARY ppi_assignments eq_ref customerID customerID 8 ppimm.ppi_loan.customerID 1 
1 PRIMARY users eq_ref PRIMARY PRIMARY 8 ppimm.ppi_assignments.agentID 1 
1 PRIMARY ppi_status ref status,customerID customerID 8 ppimm.ppi_loan.customerID 6 
1 PRIMARY ppi_statuses eq_ref PRIMARY PRIMARY 4 ppimm.ppi_status.status 1 Using where; Using index 
3 DEPENDENT SUBQUERY ppi_ques_status ref loanID,status loanID 8 func 1 Using where; Using filesort 
2 DEPENDENT SUBQUERY ppi_sar_status ref loanID,status,history loanID 8 func 2 Using where 

? 내가 생성 한 결과가 모두 필요하므로 하위 쿼리를 제거 할 수 없습니다.

+0

'ppi_ques_status' 테이블에서만 단일 필드 인덱스가있는 반면, 실제로 쿼리를 돕기 위해서는 다중 필드 인덱스가 필요하다는 것은 명백합니다. – Shadow

+1

많은 행이 데이터베이스에 지정한 조건을 충족하므로 많은 행을 스캔합니다.'where where'는 인덱스를 적용하여 조회해야하는 행의 수를 줄이는 것을 의미하며'filesorts '를 사용하는 것은 거기에있는 레코드를 비트순으로 정렬하여 지정한 순서대로 반환하기 때문에 (filesort가 잘못 명명되었습니다) . 메모리 (빠른) 대신 디스크 (느린)에서 읽으므로 속도가 느립니다. – Mjh

+0

@mjh 설명에 대한 해석은 실제 의미와 다릅니다. – Shadow

답변

0

주석에서 이미 언급했듯이 느린 쿼리의 주된 원인은 단일 열 인덱스 만있는 것처럼 보이는 반면, 조인, 필터 및 그룹을 포괄하는 다중 열 인덱스가 필요합니다. min()group by 2 필드는, 여러 가지 다른 필드는 집계 함수의 대상이되지 않고 select 목록에 나열됩니다 비록 당신 불구하고

  1. :

    또한, 쿼리는 2 다른 문제가 있습니다. MySQL은 특정 SQL 모드 설정에서 이러한 쿼리를 실행할 수 있지만 실제로 SQL 표준을 따르지 않으며 예기치 않은 부작용이있을 수 있습니다.

  2. left join의 왼쪽 테이블 인 조인 조건의 ppi_loan 테이블에 필터가 있습니다. 왼쪽 조인의 특성으로 인해 이러한 레코드는 결과 집합에서 제거되지 않지만 MySQL은 값을 조인하지 않습니다. 이러한 기준은 where 절로 이동해야합니다.

인덱스 나는 만들 것이다 :

  • ppi_sar_status을 : loanID, 상태, 역사 필드에 다중 열 인덱스 -이 테이블이

    하지 않기 때문에 내가 가입 섹션이 이동 고려할 것
  • ppi_ques_status : loanID, status, timestamp 필드의 다중 열 인덱스 - 하위 쿼리와 조인을 모두 지원합니다. 기억할 것은, 부질의는 또한 설명에 filesort를 가지고 있다는 것입니다.

  • ppi_loan : 최소한 group by 절을 지원하는 customerID, loan_number 필드의 다중 열 인덱스로, 따라서 filesort를 최소한으로 피하십시오. 이 인덱스에 대한 선택성에 따라 조인 기준의 다른 필드를 추가하는 것을 고려할 수 있습니다.

조인에서 마지막 2 개의 상태 테이블이있는 이유는 사용자가 이들 값을 검색하지 않기 때문입니다. 이러한 테이블을 사용하여 특정 레코드를 제거하는 경우 조인 대신 하위 쿼리 exists()을 사용하는 것이 좋습니다. 조인 할 때 MySQL은 조인 된 모든 테이블에서 데이터를 가져와야하지만 exists() 하위 쿼리에서는 기본 테이블에서 실제 데이터를 검색하지 않고 결과 세트에 최소 하나의 레코드 만 있는지 확인합니다.

+1

쿼리가 I/O/CPU 바운드이거나 반환 할 데이터가 많기 때문에 쿼리 속도가 느립니다. 버퍼를 사용하고 네트워크를 통해 보내야합니다. 귀하의 대답은 오직 색인만을 다루고 있습니다. 전송할 데이터의 양은 아무 것도하지 않으며 레코드를 찾기 위해 얼마나 많은 I/O가 낭비 될지를보다 효과적으로 줄일 수 있습니다. 90k 행 또는 심지어 1m 행은 오늘날의 CPU에서는 아무것도 아니며, 이는 innodb 기본 설정이 사용되었음을 분명히 나타냅니다. 인덱스를보다 효율적으로 만들 수 있지만 문제의 한 부분에만 집중했습니다. 또한 하위 쿼리 === JOIN. 나는 이것에 투표하지 않을 것이다. – Mjh

+0

그림자, 고맙습니다. 내일 보겠습니다. – swdee

+0

OP가 표시된대로 @mjh, 쿼리는 755 개의 레코드를 생성하므로 버퍼링과 네트워크를 통한 종료는 문제가되지 않습니다. 하위 쿼리는 조인과 동일하지 않습니다. – Shadow