diff --git a/mod/quiz/report/attemptsreport.php b/mod/quiz/report/attemptsreport.php index 4a4698f2b97..676161ba7fe 100644 --- a/mod/quiz/report/attemptsreport.php +++ b/mod/quiz/report/attemptsreport.php @@ -614,13 +614,21 @@ abstract class quiz_attempt_report_table extends table_sql { 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 cdcaf8f07da..2f41b12d7ff 100644 --- a/question/engine/datalib.php +++ b/question/engine/datalib.php @@ -865,33 +865,42 @@ ORDER BY 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 + /** + * 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 {question_attempts} {$alias}qa - JOIN {question_attempt_steps} {$alias}qas ON - {$alias}qas.id = {$this->latest_step_for_qa_subquery($alias . 'qa.id')} - ) $alias"; + 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') {