2011-07-27 158 views
3

無論環境如何,以下查詢都需要超過30秒才能進行計算。用於極慢查詢的MySQL索引

SELECT COUNT(r.response_answer) 
FROM response r 
INNER JOIN (
SELECT G.question_id 
FROM question G 
INNER JOIN answer_group AG ON G.answer_group_id = AG.answer_group_id 
WHERE AG.answer_group_stat = 'statistic' 
) AS q ON r.question_id = q.question_id 
INNER JOIN org_survey os ON os.org_survey_code = r.org_survey_code 
WHERE os.survey_id =42 
AND r.response_answer = 5 
AND DATEDIFF(NOW() , r.added_dt) <1000000 
AND r.uuid IS NOT NULL 

當我解釋查詢,

id select_type table type possible_keys key key_len ref rows Extra 
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 1087  
1 PRIMARY r ref question_id,org_survey_code,code_question,uuid,uor question_id 4 q.question_id 1545 Using where 
1 PRIMARY os eq_ref org_survey_code,survey_id,org_survey_code_2 org_survey_code 12 survey_2.r.org_survey_code 1 Using where 
2 DERIVED G ALL agid NULL NULL NULL 1680  
2 DERIVED AG eq_ref PRIMARY PRIMARY 1 survey_2.G.answer_group_id 1 Using where 

我有索引的一個非常基本的知識,但我已經試過幾乎所有的組合,我能想到的似乎並不能改善查詢的速度。答案表是在200萬行左右,問題是大約1500行,answer_group大約是50,org_survey大約是8,000。

下面是每個基本結構:

CREATE TABLE `response` (
`response_id` int(10) unsigned NOT NULL auto_increment, 
`response_answer` text NOT NULL, 
`question_id` int(10) unsigned NOT NULL default '0', 
`org_survey_code` varchar(7) NOT NULL, 
`uuid` varchar(40) default NULL, 
`added_dt` datetime default NULL, 
PRIMARY KEY (`response_id`), 
KEY `question_id` (`question_id`), 
KEY `org_survey_code` (`org_survey_code`), 
KEY `code_question` (`org_survey_code`,`question_id`), 
KEY `IDX_ADDED_DT` (`added_dt`), 
KEY `uuid` (`uuid`), 
KEY `response_answer` (`response_answer`(1)), 
KEY `response_question` (`response_answer`(1),`question_id`), 
) ENGINE=MyISAM AUTO_INCREMENT=2298109 DEFAULT CHARSET=latin1 

CREATE TABLE `question` (
`question_id` int(10) unsigned NOT NULL auto_increment, 
`question_text` varchar(250) NOT NULL default '', 
`question_group` varchar(250) default NULL, 
`question_position` tinyint(3) unsigned NOT NULL default '0', 
`survey_id` tinyint(3) unsigned NOT NULL default '0', 
`answer_group_id` mediumint(8) unsigned NOT NULL default '0', 
`seq_id` int(11) NOT NULL default '0', 
PRIMARY KEY (`question_id`), 
KEY `question_group` (`question_group`(10)), 
KEY `survey_id` (`survey_id`), 
KEY `agid` (`answer_group_id`) 
) ENGINE=MyISAM AUTO_INCREMENT=1860 DEFAULT CHARSET=latin1 

CREATE TABLE `org_survey` (
`org_survey_id` int(11) NOT NULL auto_increment, 
`org_survey_code` varchar(10) NOT NULL default '', 
`org_id` int(11) NOT NULL default '0', 
`org_manager_id` int(11) NOT NULL default '0', 
`org_url_id` int(11) default '0', 
`division_id` int(11) default '0', 
`sector_id` int(11) default NULL, 
`survey_id` int(11) NOT NULL default '0', 
`process_batch` tinyint(4) default '0', 
`added_dt` datetime default NULL, 
PRIMARY KEY (`org_survey_id`), 
UNIQUE KEY `org_survey_code` (`org_survey_code`), 
KEY `org_id` (`org_id`), 
KEY `survey_id` (`survey_id`), 
KEY `org_survey_code_2` (`org_survey_code`,`total_taken`), 
KEY `org_manager_id` (`org_manager_id`), 
KEY `sector_id` (`sector_id`) 
) ENGINE=MyISAM AUTO_INCREMENT=9268 DEFAULT CHARSET=latin1 

CREATE TABLE `answer_group` (
`answer_group_id` tinyint(3) unsigned NOT NULL auto_increment, 
`answer_group_name` varchar(50) NOT NULL default '', 
`answer_group_type` varchar(20) NOT NULL default '', 
`answer_group_stat` varchar(20) NOT NULL default 'demographic', 
PRIMARY KEY (`answer_group_id`) 
) ENGINE=MyISAM AUTO_INCREMENT=53 DEFAULT CHARSET=latin1 

我知道有小東西,我可能可以做,以提高數據庫的效率,如減少整數的大小發生是不必要的。然而,考慮到在這裏產生結果所花費的荒謬時間,這些都是相當微不足道的。根據解釋向我展示的內容,我該如何正確索引這些表格?似乎我已經嘗試了大量的組合,無濟於事。此外,還有其他任何人都可以看到將優化表並減少查詢嗎?我需要它在不到一秒的時間內計算出來。提前致謝!

回答

1

你可以嘗試下面的查詢嗎?我已經刪除了原來的子查詢。這可能會讓優化器產生更好的執行計劃。

SELECT COUNT(r.response_answer) 
FROM response r 
    INNER JOIN question q  ON r.question_id = q.question_id 
    INNER JOIN answer_group ag ON q.answer_group_id = ag.answer_group_id 
    INNER JOIN org_survey os ON os.org_survey_code = r.org_survey_code 
WHERE 
     ag.answer_group_stat = 'statistic' 
    AND os.survey_id = 42 
    AND r.response_answer = 5 
    AND DATEDIFF(NOW(), r.added_dt) < 1000000 
    AND r.uuid IS NOT NULL 
5

1.如果您想使用的r.added_dt的指標,而不是:

DATEDIFF(NOW(), r.added_dt) < 1000000 

使用:

CURDATE() - INTERVAL 1000000 DAY < r.added_dt 

總之,上述條件檢查是否added_at百萬天老或沒有。你真的儲存這麼久的日期嗎?如果沒有,你可以簡單地刪除這個條件。

如果你想要這個條件,added_at索引將有很大幫助。您現在的查詢將檢查所有行的這種情況,調用DATEDIFF()函數的次數與response表的行次數相同。


2.Since r.response_answer不能NULL,而不是:

SELECT COUNT(r.response_answer) 

使用:

SELECT COUNT(*) 

COUNT(*)COUNT(field)更快。


3。您用於連接表的三個字段中的兩個有不同的數據類型:

ON  question . answer_group_id 
    = answer_group . answer_group_id 

CREATE TABLE question (
    ... 
    answer_group_id mediumint(8) ...,    <--- mediumint 

CREATE TABLE answer_group (
    answer_group_id` tinyint(3) ...,    <--- tinyint 

------------------------------- 

ON org_survey . org_survey_code 
    = response . org_survey_code 

CREATE TABLE response (
    ... 
    org_survey_code varchar(7) NOT NULL,    <--- 7 

CREATE TABLE org_survey (
    ... 
    org_survey_code varchar(10) NOT NULL default '', <--- 10 

數據類型mediumint是不一樣的tinyint和同樣爲varchar(7)varchar(10)。當它們用於連接時,MySQL不得不浪費時間從一種類型轉換爲另一種類型。轉換它們中的一個,使它們具有相同的數據類型。這不是查詢的主要問題,但此更改還可以幫助所有其他使用這些連接的查詢。

做出這個改變後,爲表格做一個'分析表'。這將有助於MySQL制定更好的執行計劃。


你有一個response_answer = 5條件,其中response_answertext。這不是一個錯誤,但最好使用response_answer = '5'(如果不這樣做的話,5'5'的轉換將由MySQL完成)。

真正的問題是,您在WHERE條件中使用的3個字段上沒有複合索引。試着增加這樣一條:

ALTER TABLE response 
    ADD INDEX ind_u1_ra1_aa 
     (uuid(1), response_answer(1), added_at) ; 

(這可能需要一段時間,因爲你的表是不小)