2012-07-06 4 views
0

날짜 범위 검색을 통해 여러 테이블을 조인하는 쿼리를 실행하고이를 더 최적화하는 방법을 찾으려 고 노력했습니다. 'mysql - 잘못된 인덱스로 인해 filesort가 발생하지 않습니다 ..?

| keyword_spent | CREATE TABLE `keyword_spent` (
    `id` bigint(20) NOT NULL auto_increment, 
    `summary_date` date NOT NULL, 
    `adgroup_id` bigint(20) NOT NULL, 
    `keyword_id` bigint(20) NOT NULL, 
    `billed_clicks` int(11) default NULL, 
    `un_billed_clicks` int(11) default NULL, 
    `spent` decimal(20,5) default NULL, 
    `last_click_recno` bigint(20) default NULL, 
    `campaign_id` bigint(20) NOT NULL, 
    `account_id` bigint(20) NOT NULL, 
    `total_convs` bigint(20) unsigned default '0', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `keyword_spent_uniq` (`summary_date`,`adgroup_id`,`keyword_id`), 
    KEY `idx_account_id` (`account_id`), 
    KEY `idx_kw_id` (`keyword_id`), 
    KEY `adgroup_id` (`adgroup_id`), 
    KEY `campaign_id` (`campaign_id`), 
    KEY `summary_date` (`summary_date`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 | 

내가 돈 그것을

+----+-------------+-------+--------+----------------------------+--------------+---------+---------------------------------+--------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys    | key   | key_len | ref        | rows | Extra          | 
+----+-------------+-------+--------+----------------------------+--------------+---------+---------------------------------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | SPENT | range | summary_date    | summary_date | 3  | NULL       | 752191 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | KW | eq_ref | PRIMARY,FK1948D0E6ED3A5544 | PRIMARY  | 8  | clicksummarydb.SPENT.keyword_id |  1 |            | 
| 1 | SIMPLE  | ADG | eq_ref | PRIMARY,FKBBC2083C29112FD0 | PRIMARY  | 8  | advertisedb.KW.adgroup_id  |  1 |            | 
| 1 | SIMPLE  | CAMP | eq_ref | PRIMARY,FKF7A90110246F33C4 | PRIMARY  | 8  | advertisedb.ADG.campaign_id  |  1 |            | 
| 1 | SIMPLE  | ACC | eq_ref | PRIMARY     | PRIMARY  | 8  | advertisedb.CAMP.account_id  |  1 |            | 
+----+-------------+-------+--------+----------------------------+--------------+---------+---------------------------------+--------+----------------------------------------------+ 

keyword_spent 테이블이 여기에 150 만 명 이상의 행을 포함하고 쇼가 생성되는 테이블 -

SELECT ACC.name AS account_name, CAMP.account_id AS account_id,CAMP.name AS campaign_name,CAMP.id AS campaign_id,ADG.id AS adgroup_id,ADG.name AS adgroup_name,KW.text AS keyword_name, 
SUM(SPENT.billed_clicks) AS billed_clicks,KW.id AS keyword_id,KW.status_id AS status_id FROM account ACC, campaign CAMP,adgroup ADG,adgroup_keyword KW INNER JOIN keyword_spent SPENT 
ON KW.id = SPENT.keyword_id WHERE  summary_date >= '2012-03-01' AND summary_date <= '2012-03-04' AND KW.adgroup_id = ADG.id AND ADG.campaign_id = CAMP.id AND CAMP.account_id = ACC.id 
GROUP BY keyword_id 

은이에 EXPLAIN은 다음을 산출한다 해당 날짜 범위에 100,000 개가 넘는 레코드가 없을 때 75 만 개의 행을 스캔하는 이유를 이해합니다.

또한 인덱스를 사용하는 대신 파일 포트를 사용하는 이유는 무엇입니까? ?

+1

먼저 할 일은 A, B, C 항목을 제거하고 내부 조인을 사용하는 것입니다. 각 항목에 대해 where 절이 summary_date에 있습니다. –

+0

@ 토니 : 동의합니다. 나는 comma 스타일 join sytnax를 사용하는 것보다'JOIN ... ON' 구문을 선호한다. BTW ... summary_date에 대한 술어는 JOIN의 ON 절에 쉽게 포함될 수 있고 WHERE 절에있을 필요가 없으며 WHERE 절이 전혀 필요하지 않습니다. – spencer7593

+0

두 제안에 따라 쿼리를 다시 정렬합니다. 그냥 궁금해서 청소기를 찾는 것 외에도 퍼프 부스트를 향상시킬 수 있습니까? –

답변

1

파일 정렬이 반드시 나쁜 것은 아닙니다. Baron Schwartz's blog post에 표시된 것처럼 파일 정렬은 반드시 파일에 관한 것은 아닙니다. 사용 가능한 유효한 인덱스가 없을 때 사용되는 빠른 정렬입니다.

최적화 방법에 대한 아이디어로서 : 모든 집계 데이터를 자체 하위 쿼리에 포함시키고 해당 데이터를 조인 할 수 있습니까? 나는 이런 식으로 생각하고있다. (필요에 따라 조절한다.)

SELECT ACC.name AS account_name, 
CAMP.account_id AS account_id, 
CAMP.name AS campaign_name, 
CAMP.id AS campaign_id, 
ADG.id AS adgroup_id, 
ADG.name AS adgroup_name, 
KW.text AS keyword_name, 
KW.id AS keyword_id, 
JOINED.billed_clicks AS billed_clicks, 
JOINED.un_billed_clicks AS un_billed_clicks, 
JOINED.total_clicks AS total_clicks, 
JOINED.spent AS spent, 
JOINED.total_convs AS total_convs 
FROM account ACC 
INNER JOIN campaign CAMP ON ACC.id = CAMP.account_id 
INNER JOIN adgroup ADG ON CAMP.id = ADG.campaign_id 
INNER JOIN adgroup_keyword KW ON ADG.id = KW.adgroup_id 
INNER JOIN (SELECT 
    SUM(billed_clicks) AS billed_clicks, 
    SUM(un_billed_clicks) AS un_billed_clicks, 
    SUM(billed_clicks) + SUM(un_billed_clicks) AS total_clicks, 
    SUM(spent) AS spent, 
    SUM(total_convs) AS total_convs, 
    id AS keyword_id 
    FROM keyword_spent 
    GROUP BY keyword_id 
) JOINED ON JOINED.keyword_id = KW.id 

나는이 권리를 잘 알고있다. 이 솔루션에는 하나의 이점이 있습니다. 그룹 별/집계는 별도로 유지되므로 원래 예제에서는 사용하지 않았던 다른 열을 기준으로 그룹에 대해 걱정할 필요가 없습니다.

+0

그게 재미있는 생각이야. 그것을 시도 할 것이다. –

+0

@ Wolfmann2000, 검색어 모델을 사용하여 (keyword_Id, summary_date)에 합성 색인을 생성하여 실행 시간을 35 초에서 6 초로 단축 시켰습니다. !! 그래서 고마워. 그러나 임은 재 배열과 별개로 내 버전과 비교하여 실행 측면에서이 쿼리가 어떻게 다른지 이해할 수 없습니다. 이해 좀 도와 주실 수 있습니까? ? 실제로 쿼리에 대한 EXPLAIN은 추가 검색을 보여 주므로 혼란 스럽습니다. –

2

조인 술어에서 참조 된 모든 컬럼에 인덱스를보십시오 : 당신도 모두 포함 커버링 인덱스를 만들 수 있습니다 - 또는

CREATE INDEX keyword_spent_IX2 ON keyword_spent (keyword_id, summary_date) 

- 또는

CREATE INDEX keyword_spent_IX3 ON keyword_spent (summary_date, keyword_id) 

쿼리에서 참조 열 :

CREATE INDEX keyword_spent_IX4 ON keyword_spent (keyword_id, summary_date, 
    billed_clicks, un_billed_clicks, spent, total_convs) 

파일 작업이 GROUP BY 때문일 수 있습니다.

필자가 선호하는 것은 구 학교 쉼표가 아닌 JOIN ... ON 구문을 사용하고 WHERE 절에 조인 조건자를 혼합하는 것입니다.

FROM account ACC 
    JOIN campaign CAMP ON CAMP.account_id = ACC.id 
    JOIN adgroup ADG ON ADG.campaign_id = CAMP.id 
    JOIN adgroup_keyword KW ON KW.adgroup_id = ADG.id 
    JOIN keyword_spent SPENT ON SPENT.keyword_id = KW.id 
WHERE SPENT.summary_date >= '2012-03-01' 
    AND SPENT.summary_date <= '2012-03-04' 
GROUP BY SPENT.id 

SELECT 목록에서 비 집계의 하위 집합으로 만 그룹화합니다. 대부분의 다른 RDBMS는 예외를 throw합니다. MySQL은 더욱 진보적입니다.

+0

아 .. 나는 keyword_id가 색인되어야한다는 것을 알지 못했습니다. 나는 그것을 시도하고 그것이 어떻게되는지 보게 될 것이다. –

+0

@Anand : filesort를 피하려면 "GROUP BY"열이 선행하는 열로 색인을 시도하십시오. – spencer7593

+0

내 원래 쿼리가 keyword_id로 그룹화 된 이후 인덱스 (keyword_id, summary_date)를 만들었습니다. 그것은 파일 목록을 제거하지 못했습니다. –

1

summary_date에 대한 색인으로 먼저 시도해보십시오 (여기서 사용됨). 그런 다음 keyword_id; 명시 적으로 가입 내부의 날짜 범위를 이동 : 또한

ON (SPENT.id = KW.id AND SPENT.summary_date BETWEEN ... AND ...) 

은 적립 당신 집계 필드를주는 VIEW를 만들어보십시오. 이상적으로 이것은 옵티 마이저에 의해 더 잘 이해되어 져야하고 약간의 시간을 절약해야합니다.

CREATE VIEW SPENT AS SELECT 
    keyword_id, 
    SUM(SPENT.billed_clicks) AS billed_clicks, 
    SUM(SPENT.un_billed_clicks) AS un_billed_clicks, 
    SUM(SPENT.spent) AS spent, 
    SUM(SPENT.total_convs) AS total_convs 
FROM keyword_spent GROUP BY keyword_id; 

는 제 keyword_id에 인덱스를 필요로하며, 제 summary_date 및 100,000 행 SELECT 동등해야보기 JOIN.

관련 문제