From 94c0ec215925dd52a5c8ea8ba59e62ef7c236c5d Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Tue, 8 Nov 2011 16:53:15 +0000 Subject: [PATCH] MDL-30185 question engine reporting: add redundant where to query for perf. Without this, MySQL fails to cope. --- mod/quiz/report/attemptsreport.php | 10 ++++- question/engine/datalib.php | 63 +++++++++++++++++------------- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/mod/quiz/report/attemptsreport.php b/mod/quiz/report/attemptsreport.php index 89bfefc1fc5ee..f26b2ec707127 100644 --- a/mod/quiz/report/attemptsreport.php +++ b/mod/quiz/report/attemptsreport.php @@ -621,13 +621,21 @@ protected function add_latest_state_join($slot) { return; } + // This condition roughly filters the list of attempts to be considered. + // It is only used in a subselect to help crappy databases (see MDL-30122) + // therefore, it is better to use a very simple join, which may include + // too many records, than to do a super-accurate join. + $qubaids = new qubaid_join("{quiz_attempts} {$alias}quiza", "{$alias}quiza.uniqueid", + "{$alias}quiza.quiz = :{$alias}quizid", array("{$alias}quizid" => $this->sql->params['quizid'])); + $dm = new question_engine_data_mapper(); - $inlineview = $dm->question_attempt_latest_state_view($alias); + list($inlineview, $viewparams) = $dm->question_attempt_latest_state_view($alias, $qubaids); $this->sql->fields .= ",\n$fields"; $this->sql->from .= "\nLEFT JOIN $inlineview ON " . "$alias.questionusageid = quiza.uniqueid AND $alias.slot = :{$alias}slot"; $this->sql->params[$alias . 'slot'] = $slot; + $this->sql->params = array_merge($this->sql->params, $viewparams); } /** diff --git a/question/engine/datalib.php b/question/engine/datalib.php index cdcaf8f07da0f..2f41b12d7ff72 100644 --- a/question/engine/datalib.php +++ b/question/engine/datalib.php @@ -865,33 +865,42 @@ public function sum_usage_marks_subquery($qubaid) { END) = 0"; } - public function question_attempt_latest_state_view($alias) { - return "( - SELECT - {$alias}qa.id AS questionattemptid, - {$alias}qa.questionusageid, - {$alias}qa.slot, - {$alias}qa.behaviour, - {$alias}qa.questionid, - {$alias}qa.variant, - {$alias}qa.maxmark, - {$alias}qa.minfraction, - {$alias}qa.flagged, - {$alias}qa.questionsummary, - {$alias}qa.rightanswer, - {$alias}qa.responsesummary, - {$alias}qa.timemodified, - {$alias}qas.id AS attemptstepid, - {$alias}qas.sequencenumber, - {$alias}qas.state, - {$alias}qas.fraction, - {$alias}qas.timecreated, - {$alias}qas.userid - - FROM {question_attempts} {$alias}qa - JOIN {question_attempt_steps} {$alias}qas ON - {$alias}qas.id = {$this->latest_step_for_qa_subquery($alias . 'qa.id')} - ) $alias"; + /** + * Get a subquery that returns the latest step of every qa in some qubas. + * Currently, this is only used by the quiz reports. See + * {@link quiz_attempt_report_table::add_latest_state_join()}. + * @param string $alias alias to use for this inline-view. + * @param qubaid_condition $qubaids restriction on which question_usages we + * are interested in. This is important for performance. + * @return array with two elements, the SQL fragment and any params requried. + */ + public function question_attempt_latest_state_view($alias, qubaid_condition $qubaids) { + return array("( + SELECT {$alias}qa.id AS questionattemptid, + {$alias}qa.questionusageid, + {$alias}qa.slot, + {$alias}qa.behaviour, + {$alias}qa.questionid, + {$alias}qa.variant, + {$alias}qa.maxmark, + {$alias}qa.minfraction, + {$alias}qa.flagged, + {$alias}qa.questionsummary, + {$alias}qa.rightanswer, + {$alias}qa.responsesummary, + {$alias}qa.timemodified, + {$alias}qas.id AS attemptstepid, + {$alias}qas.sequencenumber, + {$alias}qas.state, + {$alias}qas.fraction, + {$alias}qas.timecreated, + {$alias}qas.userid + + FROM {$qubaids->from_question_attempts($alias . 'qa')} + JOIN {question_attempt_steps} {$alias}qas ON + {$alias}qas.id = {$this->latest_step_for_qa_subquery($alias . 'qa.id')} + WHERE {$qubaids->where()} + ) $alias", $qubaids->from_where_params()); } protected function latest_step_for_qa_subquery($questionattemptid = 'qa.id') {