Skip to content
Browse files

MDL-37577 quiz overdue handling: optimise database query

This is the standard trick of adding a redundant WHERE clause to a
subquery to help the database realise that it does not have to inspect
so many rows.
  • Loading branch information...
1 parent 7def533 commit 876ba8faec0fc7d04072cc5f6fd4e33d3c25042e @timhunt timhunt committed
Showing with 35 additions and 18 deletions.
  1. +3 −2 mod/quiz/cronlib.php
  2. +32 −16 mod/quiz/locallib.php
View
5 mod/quiz/cronlib.php
@@ -100,7 +100,8 @@ public function get_list_of_overdue_attempts($processto) {
// SQL to compute timeclose and timelimit for each attempt:
- $quizausersql = quiz_get_attempt_usertime_sql();
+ $quizausersql = quiz_get_attempt_usertime_sql(
+ "iquiza.state IN ('inprogress', 'overdue') AND iquiza.timecheckstate <= :iprocessto");
// This query should have all the quiz_attempts columns.
return $DB->get_recordset_sql("
@@ -116,6 +117,6 @@ public function get_list_of_overdue_attempts($processto) {
AND quiza.timecheckstate <= :processto
ORDER BY quiz.course, quiza.quiz",
- array('processto' => $processto));
+ array('processto' => $processto, 'iprocessto' => $processto));
}
}
View
48 mod/quiz/locallib.php
@@ -783,34 +783,48 @@ function quiz_update_open_attempts(array $conditions) {
}
$params = array();
- $coursecond = '';
- $usercond = '';
- $quizcond = '';
- $groupcond = '';
+ $wheres = array("quiza.state IN ('inprogress', 'overdue')");
+ $iwheres = array("iquiza.state IN ('inprogress', 'overdue')");
if (isset($conditions['courseid'])) {
list ($incond, $inparams) = $DB->get_in_or_equal($conditions['courseid'], SQL_PARAMS_NAMED, 'cid');
$params = array_merge($params, $inparams);
- $coursecond = "AND quiza.quiz IN (SELECT q.id FROM {quiz} q WHERE q.course $incond)";
+ $wheres[] = "quiza.quiz IN (SELECT q.id FROM {quiz} q WHERE q.course $incond)";
+ list ($incond, $inparams) = $DB->get_in_or_equal($conditions['courseid'], SQL_PARAMS_NAMED, 'icid');
+ $params = array_merge($params, $inparams);
+ $iwheres[] = "iquiza.quiz IN (SELECT q.id FROM {quiz} q WHERE q.course $incond)";
}
+
if (isset($conditions['userid'])) {
list ($incond, $inparams) = $DB->get_in_or_equal($conditions['userid'], SQL_PARAMS_NAMED, 'uid');
$params = array_merge($params, $inparams);
- $usercond = "AND quiza.userid $incond";
+ $wheres[] = "quiza.userid $incond";
+ list ($incond, $inparams) = $DB->get_in_or_equal($conditions['userid'], SQL_PARAMS_NAMED, 'iuid');
+ $params = array_merge($params, $inparams);
+ $iwheres[] = "iquiza.userid $incond";
}
+
if (isset($conditions['quizid'])) {
list ($incond, $inparams) = $DB->get_in_or_equal($conditions['quizid'], SQL_PARAMS_NAMED, 'qid');
$params = array_merge($params, $inparams);
- $quizcond = "AND quiza.quiz $incond";
+ $wheres[] = "quiza.quiz $incond";
+ list ($incond, $inparams) = $DB->get_in_or_equal($conditions['quizid'], SQL_PARAMS_NAMED, 'iqid');
+ $params = array_merge($params, $inparams);
+ $iwheres[] = "iquiza.quiz $incond";
}
+
if (isset($conditions['groupid'])) {
list ($incond, $inparams) = $DB->get_in_or_equal($conditions['groupid'], SQL_PARAMS_NAMED, 'gid');
$params = array_merge($params, $inparams);
- $groupcond = "AND quiza.quiz IN (SELECT qo.quiz FROM {quiz_overrides} qo WHERE qo.groupid $incond)";
+ $wheres[] = "quiza.quiz IN (SELECT qo.quiz FROM {quiz_overrides} qo WHERE qo.groupid $incond)";
+ list ($incond, $inparams) = $DB->get_in_or_equal($conditions['groupid'], SQL_PARAMS_NAMED, 'igid');
+ $params = array_merge($params, $inparams);
+ $iwheres[] = "iquiza.quiz IN (SELECT qo.quiz FROM {quiz_overrides} qo WHERE qo.groupid $incond)";
}
// SQL to compute timeclose and timelimit for each attempt:
- $quizausersql = quiz_get_attempt_usertime_sql();
+ $quizausersql = quiz_get_attempt_usertime_sql(
+ implode("\n AND ", $iwheres));
// SQL to compute the new timecheckstate
$timecheckstatesql = "
@@ -822,11 +836,7 @@ function quiz_update_open_attempts(array $conditions) {
CASE WHEN quiza.state = 'overdue' THEN quiz.graceperiod ELSE 0 END";
// SQL to select which attempts to process
- $attemptselect = " quiza.state IN ('inprogress', 'overdue')
- $coursecond
- $usercond
- $quizcond
- $groupcond";
+ $attemptselect = implode("\n AND ", $wheres);
/*
* Each database handles updates with inner joins differently:
@@ -876,9 +886,14 @@ function quiz_update_open_attempts(array $conditions) {
/**
* Returns SQL to compute timeclose and timelimit for every attempt, taking into account user and group overrides.
*
- * @return string SQL select with columns attempt.id, usertimeclose, usertimelimit
+ * @param string $redundantwhereclauses extra where clauses to add to the subquery
+ * for performance. These can use the table alias iquiza for the quiz attempts table.
+ * @return string SQL select with columns attempt.id, usertimeclose, usertimelimit.
*/
-function quiz_get_attempt_usertime_sql() {
+function quiz_get_attempt_usertime_sql($redundantwhereclauses = '') {
+ if ($redundantwhereclauses) {
+ $redundantwhereclauses = 'WHERE ' . $redundantwhereclauses;
+ }
// The multiple qgo JOINS are necessary because we want timeclose/timelimit = 0 (unlimited) to supercede
// any other group override
$quizausersql = "
@@ -894,6 +909,7 @@ function quiz_get_attempt_usertime_sql() {
LEFT JOIN {quiz_overrides} qgo2 ON qgo2.quiz = iquiza.quiz AND qgo2.groupid = gm.groupid AND qgo2.timeclose > 0
LEFT JOIN {quiz_overrides} qgo3 ON qgo3.quiz = iquiza.quiz AND qgo3.groupid = gm.groupid AND qgo3.timelimit = 0
LEFT JOIN {quiz_overrides} qgo4 ON qgo4.quiz = iquiza.quiz AND qgo4.groupid = gm.groupid AND qgo4.timelimit > 0
+ $redundantwhereclauses
GROUP BY iquiza.id, iquiz.id, iquiz.timeclose, iquiz.timelimit";
return $quizausersql;
}

0 comments on commit 876ba8f

Please sign in to comment.
Something went wrong with that request. Please try again.