【发布时间】:2011-02-19 00:34:37
【问题描述】:
我正在做一个包含很多包含的相当复杂的查找,其中 rails 正在拆分为一系列离散查询,而不是执行单个大连接。查询真的很慢——我的数据集并不庞大,没有一个表有超过几千条记录。
我已经为查询中检查的所有字段编制了索引,但我担心索引由于某种原因没有帮助:我安装了一个名为“query_reviewer”的插件,它查看用于构建页面的查询,并列出他们的问题。这表明没有使用索引,并且它具有在查询中调用“解释”的结果,其中列出了各种问题。下面是一个 find 调用示例:
Question.paginate(:all, {:page=>1, :include=>[:answers, :quizzes, :subject, {:taggings=>:tag}, {:gradings=>[:age_group, :difficulty]}], :conditions=>["((questions.subject_id = ?) or (questions.subject_id = ? and tags.name = ?))", "1", 19, "English"], :order=>"subjects.name, (gradings.difficulty_id is null), gradings.age_group_id, gradings.difficulty_id", :per_page=>30})
这里是生成的 sql 查询:
SELECT DISTINCT `questions`.id
FROM `questions`
LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `questions`.id
AND `taggings`.taggable_type = 'Question'
LEFT OUTER JOIN `tags` ON `tags`.id = `taggings`.tag_id
LEFT OUTER JOIN `subjects` ON `subjects`.id = `questions`.subject_id
LEFT OUTER JOIN `gradings` ON gradings.question_id = questions.id
WHERE (((questions.subject_id = '1') or (questions.subject_id = 19 and tags.name = 'English')))
ORDER BY subjects.name, (gradings.difficulty_id is null), gradings.age_group_id, gradings.difficulty_id
LIMIT 0, 30
SELECT `questions`.`id` AS t0_r0 <..etc...>
FROM `questions`
LEFT OUTER JOIN `answers` ON answers.question_id = questions.id
LEFT OUTER JOIN `quiz_questions` ON (`questions`.`id` = `quiz_questions`.`question_id`)
LEFT OUTER JOIN `quizzes` ON (`quizzes`.`id` = `quiz_questions`.`quiz_id`)
LEFT OUTER JOIN `subjects` ON `subjects`.id = `questions`.subject_id
LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `questions`.id
AND `taggings`.taggable_type = 'Question'
LEFT OUTER JOIN `tags` ON `tags`.id = `taggings`.tag_id
LEFT OUTER JOIN `gradings` ON gradings.question_id = questions.id
LEFT OUTER JOIN `age_groups` ON `age_groups`.id = `gradings`.age_group_id
LEFT OUTER JOIN `difficulties` ON `difficulties`.id = `gradings`.difficulty_id
WHERE (((questions.subject_id = '1') or (questions.subject_id = 19 and tags.name = 'English')))
AND `questions`.id IN (602, 634, 666, 698, 730, 762, 613, 645, 677, 709, 741, 592, 624, 656, 688, 720, 752, 603, 635, 667, 699, 731, 763, 614, 646, 678, 710, 742, 593, 625)
ORDER BY subjects.name, (gradings.difficulty_id is null), gradings.age_group_id, gradings.difficulty_id
SELECT count(DISTINCT `questions`.id) AS count_all FROM `questions`
LEFT OUTER JOIN `answers` ON answers.question_id = questions.id
LEFT OUTER JOIN `quiz_questions` ON (`questions`.`id` = `quiz_questions`.`question_id`)
LEFT OUTER JOIN `quizzes` ON (`quizzes`.`id` = `quiz_questions`.`quiz_id`)
LEFT OUTER JOIN `subjects` ON `subjects`.id = `questions`.subject_id
LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `questions`.id
AND `taggings`.taggable_type = 'Question'
LEFT OUTER JOIN `tags` ON `tags`.id = `taggings`.tag_id
LEFT OUTER JOIN `gradings` ON gradings.question_id = questions.id
LEFT OUTER JOIN `age_groups` ON `age_groups`.id = `gradings`.age_group_id
LEFT OUTER JOIN `difficulties` ON `difficulties`.id = `gradings`.difficulty_id
WHERE (((questions.subject_id = '1') or (questions.subject_id = 19 and tags.name = 'English')))
实际上,看看这些格式都很好,这里有大量的加入。这肯定不是最佳的。无论如何,看起来我有两个问题。
1) 我对这里提到的每个 id 和外键字段都有一个索引。上面的第二个查询是最慢的,在它上面调用解释(直接在 mysql 中做)给了我以下信息:
+----+-------------+----------------+--------+---------------------------------------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+--------+---------------------------------------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | questions | range | PRIMARY,index_questions_on_subject_id | PRIMARY | 4 | NULL | 30 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | answers | ref | index_answers_on_question_id | index_answers_on_question_id | 5 | millionaire_development.questions.id | 2 | |
| 1 | SIMPLE | quiz_questions | ref | index_quiz_questions_on_question_id | index_quiz_questions_on_question_id | 5 | millionaire_development.questions.id | 1 | |
| 1 | SIMPLE | quizzes | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.quiz_questions.quiz_id | 1 | |
| 1 | SIMPLE | subjects | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.questions.subject_id | 1 | |
| 1 | SIMPLE | taggings | ref | index_taggings_on_taggable_id_and_taggable_type,index_taggings_on_taggable_type | index_taggings_on_taggable_id_and_taggable_type | 263 | millionaire_development.questions.id,const | 1 | |
| 1 | SIMPLE | tags | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.taggings.tag_id | 1 | Using where |
| 1 | SIMPLE | gradings | ref | index_gradings_on_question_id | index_gradings_on_question_id | 5 | millionaire_development.questions.id | 2 | |
| 1 | SIMPLE | age_groups | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.gradings.age_group_id | 1 | |
| 1 | SIMPLE | difficulties | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.gradings.difficulty_id | 1 | |
+----+-------------+----------------+--------+---------------------------------------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------+------+----------------------------------------------+
query_reviewer 插件有这样的说法 - 它列出了几个问题:
Table questions: Using temporary table, Long key length (263), Using filesort
MySQL must do an extra pass to find out how to retrieve the rows in sorted order.
To resolve the query, MySQL needs to create a temporary table to hold the result.
The key used for the index was rather long, potentially affecting indices in memory
2) 看起来 rails 并没有以非常理想的方式拆分这个发现。是吗,你觉得呢?我最好手动执行几个查找查询而不是一个大的组合查询吗?
感谢任何建议,最大
【问题讨论】:
标签: sql ruby-on-rails include find