2011-11-02 3 views
1

이 쿼리를 최적화하는 데 많은 시간을 투자했지만 큰 테이블의 속도가 느려지 기 시작했습니다. 나는 이것이 아마도 최악의 질문 유형이라고 생각하지만 약간의 지침을 찾고 있습니다. 필자는 데이터베이스 스키마를 공개 할 자유가 전혀 없기 때문에 이것은 충분한 정보 일 것입니다.내부 조인을 사용하여 MySQL 쿼리 최적화

  • tblA.id 및 tblB.index_id
  • tblC.name 및 tblD.s_type
  • tblD.name과 : 덕분에,
    SELECT tblA.id, tblB.id, tblC.id, tblD.id 
    FROM tblA, tblB, tblC, tblD 
    INNER JOIN (SELECT max(tblB.id) AS xid 
           FROM tblB 
           WHERE tblB.rdd = 11305 
           GROUP BY tblB.index_id 
           ORDER BY NULL) AS rddx 
          ON tblB.id = rddx.xid 
    WHERE 
        tblA.id = tblB.index_id 
        AND tblC.name = tblD.s_type 
        AND tblD.name = tblA.s_name 
    GROUP BY tblA.s_name 
    ORDER BY NULL; 
    

    간의 일대 다 관계입니다 tblA.s_name
 
+----+-------------+------------+--------+---------------+-----------+---------+------------------------------+-------+------------------------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref       | rows | Extra      | 
+----+-------------+------------+--------+---------------+-----------+---------+------------------------------+-------+------------------------------+ 
| 1 | PRIMARY  | derived2 | ALL | NULL   | NULL  | NULL | NULL       | 32568 | Using temporary    | 
| 1 | PRIMARY  | tblB  | eq_ref | PRIMARY  | PRIMARY | 8  | rddx.xid      |  1 |        | 
| 1 | PRIMARY  | tblA  | eq_ref | PRIMARY  | PRIMARY | 8  | tblB.index_id    |  1 | Using where     | 
| 1 | PRIMARY  | tblD  | eq_ref | PRIMARY  | PRIMARY | 22  | tblA.s_name     |  1 | Using where     | 
| 1 | PRIMARY  | tblC  | eq_ref | PRIMARY  | PRIMARY | 22  | tblD.s_type     |  1 |        | 
| 2 | DERIVED  | tblB  | ref | rdd_idx  | rdd_idx | 7  |        | 65722 | Using where; Using temporary | 
+----+-------------+------------+--------+---------------+-----------+---------+------------------------------+-------+------------------------------+ 
+1

사용중인 테이블은 무엇입니까? – whudson05

+0

좀 더 자세한 정보를 추가 할 수 있습니까? 예를 들어,'WHERE tblB.rdd = 11305' 조건을 만족하는 레코드는 몇 개입니까? 실제로 65722입니까? 생성 된 임시 테이블을 제거하면 쿼리에 도움이되지만이 테이블에 무엇이 들어 있는지 모르기 때문에 실제로 말하기는 어렵습니다. – Fenton

+0

답장을 보내 주셔서 감사합니다. 테이블은 InnoDB이다. tblB에는 rdd = 11305 인 41,633 개의 항목이 있습니다. – Doug

답변

1

WHERE 절에서 조인 대신 조인을 사용하여 쿼리를 업데이트했습니다. 또한 개발자가 테이블을보고 테이블 간의 관계를 직접 볼 수 있습니다. A-> B, A-> D 및 D-> C. 이제는 일반적인 "ID = Index_ID"를 기반으로 가장 높은 ID를 원하고 RDD = 11305가 완전한 하위 쿼리를 요구하지 않는 테이블 B에서. 그러나 이것은 "MAX()"를 필드 선택 절의 상부로 옮겼습니다. tblB on (index_id, rdd)에 색인이 있는지 확인합니다. 마지막으로, STRAIGHT_JOIN을 수행하면 구체적으로 나열된 방법에 따라 쿼리를 실행하는 순서가 적용됩니다.

- COMMENT에서 편집 -

당신이 tblB에서 널 (null)을 받고 나타납니다. 이것은 일반적으로 유효한 tblA 레코드를 나타내지 만 RDD가 11305 인 동일한 ID로 tblB 레코드가 없습니다. 즉, 11305와 관련된 항목에만 관심이있는 것으로 보입니다. 따라서 그에 따라 쿼리를 조정할 것입니다. "RDD"열을 기준으로 tblB에 대한 색인이 있는지 확인하십시오 (적어도 여러 열 색인의 경우 첫 번째 위치에 있음)

위에서 볼 수 있듯이 표 B에서 사전 쿼리를 수행하고 있습니다. 11305 개의 항목에 대해서만 그리고 index_ID에 의한 사전 그룹핑 (tblA에 링크 된 것처럼). 이것은 그들이 존재할 인덱스 당 하나의 레코드를 제공합니다 ...이 결과에서 A에 다시 가입하고 다시 B로 다시 돌아가지만 가장 높은 일치 ID를 기반으로 이전에 D와 C가 발견되었습니다. 이제 테이블의 모든 열을 가져 와서 적절한 레코드를 얻을 수 있습니다.이 쿼리에는 NULL 값이 없어야합니다.

잘하면, 나는 당신을 위해 조각을 함께 가져 오는 방법을 명확히했습니다.

SELECT STRAIGHT_JOIN 
     PreQuery.HighestPerIndexID 
     tblA.id, 
     tblA.AnotherAField, 
     tblA.Etc, 
     tblB.SomeOtherField, 
     tblB.AnotherField, 
     tblC.id, 
     tblD.id 
    FROM 
     (select PQ1.Index_ID, 
       max(PQ1.ID) as HighestPerIndexID 
      from tblB PQ1 
      where PQ1.RDD = 11305 
      group by PQ1.Index_ID) PreQuery 

     JOIN tblA 
      on PreQuery.Index_ID = tblA.ID 

     join tblB 
      on PreQuery.HighestPerIndexID = tblB.ID 

     join tblD 
      on tblA.s_Name = tblD.name 

      join tblC 
       on tblD.s_type = tblC.Name 
    ORDER BY 
     tblA.s_Name 
+0

도움 주셔서 감사합니다. 이 제안은 제게는 효과가 없었지만 새로운 방향으로 생각하고 있습니다. 나는 당신이 이미 이해하고 있다고 생각하지만 약간은 분명히 해두겠습니다. tblA와 tblB 사이에 일대 다 관계가 있습니다. 여기서 tblA의 주어진 엔트리는 tblB에 _n_ 연관된 엔트리가 있고, _m_는 특정 'rdd'. MAX (tblB.id) select 절의 목적은이 관계의 가장 최근 구성원을 확보하는 것입니다. 이 쿼리는 작동하는 것처럼 보이지만 조인에 사용하기 위해 가장 높은 'id'를 가진 tblB에서 항목을 선택한다고 생각하지 않습니다. 희망은 그 말이 맞습니다. – Doug

+0

@Doug, 조인은 tblB의 모든 레코드에 대해 모든 tblA에서 하나의 레코드를 가져 오므로 적절하게 표현되어야합니다. 그룹이 아닌 모든 집계는 가장 높은 항목 만 보장해야합니다. 자, 문제. 테이블 B의 다른 열을 얻으려고한다면, MySQL은 당신이 마주 치고있는 다른 열의 첫 번째 인스턴스를 잡아내는 것입니다 ... 그렇다면 정말로 당신이 무엇인지 반영하도록 쿼리를 조정할 수 있습니다 찾고. – DRapp

+0

@ DRapp- 조정은 크게 평가 될 것입니다;). 나는 쿼리를 상당히 슬림하려고했지만 실제로 tblB에서 다른 컬럼을 얻고있다. 이 메서드를 사용하면 NULL로 표시됩니다. – Doug

2

나는 정보 흐름을 오해하지 않았다면 당신이 제공 한 이온 나는 분명히 내가이이 같은 설명에 대한 것은 데이터베이스에있는 데이터에 의존 설명 제공 할 수

EXPLAIN SELECT tblA.id, MAX(tblB.id), tblC.id, tblD.id 
FROM tblA 
LEFT JOIN tblD ON tblD.name = tblA.s_name 
LEFT JOIN tblC ON tblC.name = tblD.s_type 
LEFT JOIN tblB ON tblA.id = tblB.index_id 
WHERE tblB.rdd = 11305 
ORDER BY NULL; 

을 다음과 같이 위의 쿼리를 다시 쓸 수 있습니다 생각합니다. 이 쿼리에 대한 설명을 보는 것이 흥미로울 것입니다.

분명히 설명 할 경우에만 예상되는 결과를 제공합니다. SHOW SESSION STATUS를 사용하여 실제 쿼리를 실행할 때 일어난 일에 대한 세부 사항을 제공 할 수 있습니다. 조사 할 조회를 실행하기 전에 실행하여 읽을 데이터가 있어야합니다. 그래서이 경우에 당신은 달릴 것입니다

FLUSH STATUS; 

EXPLAIN SELECT tblA.id, MAX(tblB.id), tblC.id, tblD.id 
FROM tblA 
LEFT JOIN tblD ON tblD.name = tblA.s_name 
LEFT JOIN tblC ON tblC.name = tblD.s_type 
LEFT JOIN tblB ON tblA.id = tblB.index_id 
WHERE tblB.rdd = 11305 
ORDER BY NULL; 

SHOW SESSION STATUS LIKE 'ha%'; 

이것은 쿼리가 실행될 때 실제로 일어났던 것을 보여주는 많은 지표를 제공합니다.

Handler_read_rnd_next - Number of requests to read next row in the data file 
Handler_read_key - Number of requests to read a row based on a key 
Handler_read_next - Number of requests to read the next row in key order 

이러한 값을 사용하면 두포에서 일어나는 일을 정확하게 볼 수 있습니다.

유감스럽게도 테이블에있는 데이터, 엔진 유형 및 쿼리에 사용 된 데이터 유형을 알지 못해서 어떻게 최적화 할 수 있는지 조언하기가 어렵습니다.