2011-07-27 20 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は約8000です。私は、そのようなことは不要だ整数のサイズを小さくするように、私はおそらく、データベースの効率を改善するために行うことができます小さなものがある知っている

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秒未満で計算する必要があります。前もって感謝します!

答えて

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

あなたがr.added_dtのインデックスが使用されるようにしたいのではなく、1.If:

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

使用:とにかく

CURDATE() - INTERVAL 1000000 DAY < r.added_dt 

added_atがある場合は、上記の条件がチェックされ百万年前かどうか。本当に古い日付を保存していますか?そうでない場合は、この条件を削除するだけです。

この条件が必要な場合は、added_atのインデックスが役立ちます。現在のクエリは、responseテーブルの行数だけDATEDIFF()関数を呼び出し、この条件のすべての行をチェックします。


2.Since r.response_answerNULLなり、代わりにすることはできません。

SELECT COUNT(r.response_answer) 

使用:

SELECT COUNT(*) 

COUNT(*)COUNT(field)よりも高速です。


3。あなたがテーブルを結合するために使用する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 

データ型mediuminttinyintと同じではないと同じことがvarchar(7)varchar(10)のために行きます。それらがjoinに使用されると、あるタイプから別のタイプへの変換にMySQLが時間を失わなければなりません。それらのうちの1つを変換して、同じデータ型を持つようにします。これは問合せの主な問題ではありませんが、この変更はこれらの結合を使用する他のすべての問合せにも役立ちます。

この変更を行った後、テーブルの「Analyze Table」を実行します。 mysqlがより良い実行計画を立てるのに役立ちます。


あなたはresponse_answertextあるresponse_answer = 5条件を持っています。これはエラーではありませんが、response_answer = '5'を使用する方が良いでしょう(5から'5'への変換は、MySQLによって行われます。

実際の問題は、WHERE条件で使用される3つのフィールドに複合インデックスがないことです。この1を追加してみてください:

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

(あなたのテーブルが小さくないとして、これはしばらく時間がかかる場合があります)

関連する問題