2015-01-21 3 views
-2

느린 MySQL 쿼리 (MySQL 5 이상)에서 문제가 발생했습니다. 3 개의 테이블을 생각해 봅시다 :MySQL의 하위 쿼리와 내부 조인 속도가 느림

내 목표는 하나의 쿼리와 고객 당 한 줄로 모든 주소와 연락처 정보를 수집하는 것입니다. (~에 대한 각 테이블 7000 항목)을 완료하는 데이 쿼리 130 초 동안했다

SELECT customers.id_customer, 
     customers.name, 
     X.contact AS contact, 
     Y.street, 
     Y.zipcode, 
     Y.city 
FROM customers 
LEFT JOIN 
(
    SELECT 
    GROUP_CONCAT(CONCAT(type, ': ', value) SEPARATOR ', ') AS contact, 
    id_customer 
    FROM customers_contacts 
    GROUP BY id_customer 
) AS X 
ON X.id_customer = customers.id_customer 

LEFT JOIN 
(
    SELECT 
    GROUP_CONCAT(street SEPARATOR '<br>') AS street, 
    GROUP_CONCAT(zipcode SEPARATOR '<br>') AS zipcode, 
    GROUP_CONCAT(city SEPARATOR '<br>') AS city, 
    id_customer 
    FROM customers_addresses 
    GROUP BY id_customer 
) AS Y 
ON Y.id_customer = customers.id_customer 
WHERE Y.street LIKE '%Avenue%' 
ORDER BY customers.name DESC 
LIMIT 0, 20 

까지 인 : 내 첫 번째 시도는 일부 고객 정보를 어떤 주소가 및/또는 접촉하지 않는 LEFT JOIN의를 사용하여이었다 좋은 것에서. EXPLAIN EXTENDED를 붙이는

을 제공합니다

id select_type table    type possible_keys key   key_len  ref  rows filtered Extra 
1 PRIMARY  customers   ref  name   name   3   const 4334 100.00  Using where; Using temporary; Using filesort 
1 PRIMARY  <derived2>   ALL  NULL   NULL   NULL  NULL 7793 100.00 
1 PRIMARY  <derived3>   ALL  NULL   NULL   NULL  NULL 8580 100.00  Using where 
3 DERIVED  customers_addresses index NULL   id_customer 5   NULL 8651 100.00 
2 DERIVED  customers_contacts index NULL   id_customer 4   NULL 9314 100.00 

내가 어떤 유래 게시물과 MySQL의 문서를 참조하십시오. 둘 다 INNER JOIN라고 말하는 것이 훨씬 빠릅니다. 나는 UNION ALL를 사용하여 INNER JOIN들과 LEFT JOIN 동작을 복제하려고 :

SELECT customers.id_customer, 
     customers.name, 
     X.contact AS contact, 
     Y.street, 
     Y.zipcode, 
     Y.city 
FROM customers 
INNER JOIN 
(
    SELECT 
    GROUP_CONCAT(CONCAT(type, ': ', value) SEPARATOR ', ') AS contact, 
    id_customer 
    FROM customers_contacts 
    GROUP BY id_customer 
    UNION ALL 
    SELECT 
    '' AS contact, 
    id_customer 
    FROM customers 
    WHERE id_customer NOT IN (SELECT DISTINCT id_customer FROM customers_contacts) 
) AS X 
ON X.id_customer = customers.id_customer 

INNER JOIN 
(
    SELECT 
    GROUP_CONCAT(street SEPARATOR '<br>') AS street, 
    GROUP_CONCAT(zipcode SEPARATOR '<br>') AS zipcode, 
    GROUP_CONCAT(city SEPARATOR '<br>') AS city, 
    id_customer 
    FROM customers_addresses 
    GROUP BY id_customer 
    UNION ALL 
    SELECT 
    '' AS street, 
    '' AS zipcode, 
    '' AS city, 
    id_customer 
    FROM customers 
    WHERE id_customer NOT IN (SELECT DISTINCT id_customer FROM customers_addresses) 
) AS Y 
ON Y.id_customer = customers.id_customer 
WHERE Y.street LIKE '%Avenue%' 
ORDER BY customers.name DESC 
LIMIT 0, 20 

이 쿼리는 20초하여 성능을 향상시켰다. 그러나 110 초는 여전히 용납되지 않습니다.

EXPLAIN EXTENDED를 붙이는 :

id select_type   table    type   possible_keys  key  key_len ref   rows filtered Extra 
1 PRIMARY    <derived2>   ALL   NULL    NULL  NULL NULL  8596 100.00  Using temporary; Using filesort 
1 PRIMARY    <derived5>   ALL   NULL    NULL  NULL NULL  8604 100.00  Using join buffer 
1 PRIMARY    customers   eq_ref   PRIMARY,name,name3 PRIMARY 4  Y.id_kunde 1  100.00  Using where 
5 DERIVED    customers_addresses index   NULL    id_kunde 5  NULL  8651 100.00 
6 UNION    customers   index   NULL    name2 767  NULL  8677 100.00  Using where; Using index 
7 DEPENDENT SUBQUERY customers_addresses index_subquery id_kunde   id_kunde 5  func  2  100.00  Using index 
NULL UNION RESULT  <union5,6>   ALL   NULL    NULL  NULL NULL  NULL NULL 
2 DERIVED    customers_contacts index   NULL    id_kunde 4  NULL  10411 100.00 
3 UNION    customers   index   NULL    name2 767  NULL  8677 100.00  Using where; Using index 
4 DEPENDENT SUBQUERY customers_contacts index_subquery id_kunde   id_kunde 4  func  1  100.00  Using index 
NULL UNION RESULT  <union2,3>   ALL   NULL    NULL  NULL NULL  NULL NULL 

을 그래서 여기 내 질문 : 어떻게 슈퍼 빠른 응답을 얻기 위해 이러한 쿼리 및/또는 데이터베이스 테이블 중 하나를 개선하기 위해? 나는 솔루션에 관심이있을뿐 아니라 향후 이러한 성능 저하를 방지하는 방법에 대한 전략에도 관심이 있습니다.

감사합니다.

당신이 선택 결과 (하위 쿼리), MySQL이 처음이 하위 쿼리를 실행하는 조인 쿼리를 사용할 때마다

, 다음 테이블을 만들 :

+0

"이 쿼리는 130 초 동안"Er, no. 이 쿼리는 구문이 올바르지 않습니다. – Strawberry

+0

죄송합니다. 스택 오버플로에 대한 쿼리를 정리하는 동안 WHERE 부분을 잘못 배치했습니다. 지금 올바른 (테스트 완료) – Timm

+0

'LIKE '% Avenue %''거리가 있어야 할 때'customers_addresses'에'LEFT JOIN'이 필요한 이유는 무엇입니까? –

답변

1

여기에 적용되는 일반적인 규칙으로, 당신은 다음을 말할 수 결과에서. 이를 두 번 수행하면 MySQL은 2 개의 테이블을 먼저 생성하고 결과가 끝난 후에는 테이블을 삭제한다. MySQL을위한 적절한 메모리 관리를 통해, 이것은 메모리에서 이루어진다. 그러나 이러한 테이블은 색인없이 생성됩니다. MySQL은 이러한 파생 테이블에 가장 적합한 인덱스를 결정할 수 없기 때문에 일반적으로 메모리에 생성되므로 쿼리가 매우 빠릅니다 (키를 사용하는 SELECT만큼 빠르지 않습니다).

두 개의 테이블이 완성되면 MySQL은 원래 테이블을 두 테이블에 조인해야하며, 기준에 따라 필터링 및 정렬해야하는 세 번째 테이블을 즉석에서 작성해야합니다.

이것은 성능 저하 요인입니다. 고객의 요구 사항 중 하나는 모든 고객이 한 줄만 사용해야한다는 것입니다. 그것은 데이터베이스가 정보를 저장하는 방법이 아니기 때문에 런타임에 데이터 변환 (GROUP_CONCAT 문)에 가격을 지불해야합니다. 나는 현재 MySQL 데이터베이스 엔진이 UNION 문으로하는 일을 100 % 확신하지 못하기 때문에 그러한 것에 대해서는 언급하지 않을 것이다.

사용 가능한 키에 대한 쉬운 INNER JOIN을 사용하지만 여러 주소가 결과 일 때 고객에 대해 여러 행이 표시되는 경우 성능이 많이 향상됩니다. 모든 고객의 결과와 모든 관련 주소를 해당 계층의 고객으로 쉽게 분리 할 수 ​​없다면 프로그래밍 언어 계층의 고객을 반복하여 한 번에 한 고객의 주소를 요청할 수 있습니다.

TL : DR : 요구 사항을 삭제하거나 오버 헤드와 함께 살기.