MDL-85637 question: Optimize max question version SQL query
This commit is contained in:
@@ -243,20 +243,11 @@ class custom_view extends \core_question\local\bank\view {
|
||||
}
|
||||
|
||||
// Build the where clause.
|
||||
$latestversion = 'qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be
|
||||
ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id AND v.status <> :substatus)';
|
||||
|
||||
// An additional condition is required in the subquery to account for scenarios
|
||||
// where the latest version is hidden. This ensures we retrieve the previous
|
||||
// "Ready" version instead of the hidden latest version.
|
||||
$onlyready = '((qv.status = :status))';
|
||||
$this->sqlparams = [
|
||||
'status' => question_version_status::QUESTION_STATUS_READY,
|
||||
'substatus' => question_version_status::QUESTION_STATUS_HIDDEN,
|
||||
];
|
||||
$this->sqlparams['status'] = question_version_status::QUESTION_STATUS_READY;
|
||||
$conditions = [];
|
||||
foreach ($this->searchconditions as $searchcondition) {
|
||||
if ($searchcondition->where()) {
|
||||
@@ -266,13 +257,20 @@ class custom_view extends \core_question\local\bank\view {
|
||||
$this->sqlparams = array_merge($this->sqlparams, $searchcondition->params());
|
||||
}
|
||||
}
|
||||
$majorconditions = ['q.parent = 0', $latestversion, $onlyready];
|
||||
$latestversion = 'qv2.questionbankentryid IS NULL';
|
||||
$majorconditions = ['q.parent = :parent', $latestversion, $onlyready];
|
||||
$this->sqlparams = array_merge(['parent' => 0], $this->sqlparams);
|
||||
// Get higher level filter condition.
|
||||
$jointype = isset($this->pagevars['jointype']) ? (int)$this->pagevars['jointype'] : condition::JOINTYPE_DEFAULT;
|
||||
$nonecondition = ($jointype === datafilter::JOINTYPE_NONE) ? ' NOT ' : '';
|
||||
$separator = ($jointype === datafilter::JOINTYPE_ALL) ? ' AND ' : ' OR ';
|
||||
// Build the SQL.
|
||||
$sql = ' FROM {question} q ' . implode(' ', $joins);
|
||||
$this->sqlparams['substatus'] = question_version_status::QUESTION_STATUS_HIDDEN;
|
||||
$sql = ' FROM {question} q ' . implode(' ', $joins) .
|
||||
' LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version
|
||||
AND qv2.status <> :substatus
|
||||
)';
|
||||
$sql .= ' WHERE ' . implode(' AND ', $majorconditions);
|
||||
if (!empty($conditions)) {
|
||||
$sql .= ' AND ' . $nonecondition . ' ( ';
|
||||
|
||||
@@ -110,25 +110,33 @@ class custom_category_condition_helper extends \qbank_managecategories\helper {
|
||||
'status' => question_version_status::QUESTION_STATUS_READY,
|
||||
'substatus' => question_version_status::QUESTION_STATUS_HIDDEN,
|
||||
];
|
||||
|
||||
$additionaljoins = "";
|
||||
$additionalwhere = "";
|
||||
|
||||
if ($showallversions === 0) {
|
||||
$additionaljoin = " LEFT JOIN {question_versions} qv2
|
||||
ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version
|
||||
AND qv2.status <> :substatus
|
||||
)";
|
||||
$additionalwhere = " AND qv2.questionbankentryid IS NULL";
|
||||
}
|
||||
|
||||
$sql = "SELECT c.*,
|
||||
(SELECT COUNT(1)
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE q.parent = '0'
|
||||
$statuscondition
|
||||
(SELECT COUNT(1)
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
$additionaljoin
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE q.parent = '0'
|
||||
$statuscondition
|
||||
AND c.id = qbe.questioncategoryid
|
||||
AND ($showallversions = 1
|
||||
OR (qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id AND v.status <> :substatus)
|
||||
)
|
||||
)
|
||||
) AS questioncount
|
||||
FROM {question_categories} c
|
||||
WHERE c.contextid IN ($contexts) $topwhere
|
||||
ORDER BY $sortorder";
|
||||
$additionalwhere
|
||||
) AS questioncount
|
||||
FROM {question_categories} c
|
||||
WHERE c.contextid IN ($contexts) $topwhere
|
||||
ORDER BY $sortorder";
|
||||
|
||||
return $DB->get_records_sql($sql, $params);
|
||||
}
|
||||
|
||||
@@ -750,22 +750,23 @@ class view {
|
||||
$extracondition = '';
|
||||
if (!$showhiddenquestion) {
|
||||
// If Show hidden question option is off, then we need get the latest version that is not hidden.
|
||||
$extracondition = ' AND v.status <> :hiddenstatus';
|
||||
$extracondition = ' AND qv2.status <> :hiddenstatus';
|
||||
$this->sqlparams = array_merge($this->sqlparams, ['hiddenstatus' => question_version_status::QUESTION_STATUS_HIDDEN]);
|
||||
}
|
||||
$latestversion = "qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be
|
||||
ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id $extracondition)";
|
||||
|
||||
// Get higher level filter condition.
|
||||
$jointype = isset($this->pagevars['jointype']) ? (int)$this->pagevars['jointype'] : condition::JOINTYPE_DEFAULT;
|
||||
$nonecondition = ($jointype === datafilter::JOINTYPE_NONE) ? ' NOT ' : '';
|
||||
$separator = ($jointype === datafilter::JOINTYPE_ALL) ? ' AND ' : ' OR ';
|
||||
// Build the SQL.
|
||||
$sql = ' FROM {question} q ' . implode(' ', $joins);
|
||||
$sql .= ' WHERE q.parent = 0 AND ' . $latestversion;
|
||||
$sql = ' FROM {question} q ' . implode(' ', $joins) .
|
||||
' LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version' .
|
||||
$extracondition .
|
||||
')' .
|
||||
' WHERE q.parent = :parent
|
||||
AND qv2.questionbankentryid IS NULL';
|
||||
$this->sqlparams = array_merge(['parent' => 0], $this->sqlparams);
|
||||
if (!empty($conditions)) {
|
||||
$sql .= ' AND ' . $nonecondition . ' ( ';
|
||||
$sql .= implode($separator, $conditions);
|
||||
|
||||
@@ -94,21 +94,21 @@ class question_category_selector implements renderable, templatable {
|
||||
$topwhere = $top ? '' : 'AND c.parent <> 0';
|
||||
$statuscondition = "AND (qv.status = '" . question_version_status::QUESTION_STATUS_READY . "' " .
|
||||
" OR qv.status = '" . question_version_status::QUESTION_STATUS_DRAFT . "' )";
|
||||
$substatuscondition = "AND v.status <> '" . question_version_status::QUESTION_STATUS_HIDDEN . "' ";
|
||||
$substatuscondition = "AND qv2.status <> '" . question_version_status::QUESTION_STATUS_HIDDEN . "' ";
|
||||
$sql = "SELECT c.*,
|
||||
(SELECT COUNT(1)
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version
|
||||
$substatuscondition
|
||||
)
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE q.parent = '0'
|
||||
$statuscondition
|
||||
AND c.id = qbe.questioncategoryid
|
||||
AND ({$showallversions} = 1
|
||||
OR (qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id $substatuscondition)
|
||||
)
|
||||
OR qv2.questionbankentryid IS NULL
|
||||
)
|
||||
) AS questioncount
|
||||
FROM {question_categories} c
|
||||
|
||||
+21
-15
@@ -81,22 +81,28 @@ function get_questions_category(object $category, bool $noparent, bool $recurse
|
||||
list($usql, $params) = $DB->get_in_or_equal($categorylist);
|
||||
|
||||
// Get the latest version of a question.
|
||||
$version = '';
|
||||
if ($latestversion) {
|
||||
$version = 'AND (qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be
|
||||
ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id) OR qv.version is null)';
|
||||
}
|
||||
$questions = $DB->get_records_sql("SELECT q.*, qv.status, qc.id AS category
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
|
||||
WHERE qc.id {$usql} {$npsql} {$version}
|
||||
ORDER BY qc.id, q.qtype, q.name", $params);
|
||||
$sql = "SELECT q.*, qv.status, qc.id AS category
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id";
|
||||
|
||||
if ($latestversion) {
|
||||
$sql .= " LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version
|
||||
)";
|
||||
}
|
||||
|
||||
$sql .= " JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
|
||||
WHERE qc.id {$usql} {$npsql}";
|
||||
|
||||
if ($latestversion) {
|
||||
$sql .= " AND (qv2.questionbankentryid IS NULL
|
||||
OR qv.version IS NULL)";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY qc.id, q.qtype, q.name";
|
||||
|
||||
$questions = $DB->get_records_sql($sql, $params);
|
||||
// Iterate through questions, getting stuff we need.
|
||||
$qresults = [];
|
||||
foreach ($questions as $question) {
|
||||
|
||||
@@ -643,16 +643,15 @@ class question_finder implements data_source_interface {
|
||||
$sql = "SELECT q.id, q.id AS id2
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version
|
||||
AND qv2.status = :readystatusqv
|
||||
)
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE qbe.questioncategoryid {$qcsql}
|
||||
AND q.parent = 0
|
||||
AND qv.status = :readystatus
|
||||
AND qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be
|
||||
ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id
|
||||
AND v.status = :readystatusqv)
|
||||
AND qv2.questionbankentryid IS NULL
|
||||
{$extraconditions}";
|
||||
|
||||
return $DB->get_records_sql_menu($sql, $qcparams + $extraparams);
|
||||
|
||||
@@ -120,7 +120,7 @@ final class questionbank_test extends \advanced_testcase {
|
||||
$q1v3 = $questiongenerator->update_question($q1v2, null, ['name' => 'Q1V3']);
|
||||
// The second question has the first version in status draft, the second version in status ready,
|
||||
// and third version in status draft.
|
||||
$q2v1 = $questiongenerator->create_question('numerical', null, ['name' => 'Q2V2', 'category' => $cat->id,
|
||||
$q2v1 = $questiongenerator->create_question('numerical', null, ['name' => 'Q2V1', 'category' => $cat->id,
|
||||
'status' => question_version_status::QUESTION_STATUS_DRAFT, ]);
|
||||
$q2v2 = $questiongenerator->update_question($q2v1, null, ['name' => 'Q2V2',
|
||||
'status' => question_version_status::QUESTION_STATUS_READY, ]);
|
||||
@@ -133,11 +133,19 @@ final class questionbank_test extends \advanced_testcase {
|
||||
'status' => question_version_status::QUESTION_STATUS_DRAFT, ]);
|
||||
$q3v3 = $questiongenerator->update_question($q3v2, null, ['name' => 'Q3V3',
|
||||
'status' => question_version_status::QUESTION_STATUS_DRAFT]);
|
||||
// The fourth question has the first and the second versions in status ready, and the third version in status draft.
|
||||
$q4v1 = $questiongenerator->create_question('essay', null, ['name' => 'Q4V1', 'category' => $cat->id,
|
||||
'status' => question_version_status::QUESTION_STATUS_READY, ]);
|
||||
$q4v2 = $questiongenerator->update_question($q4v1, null, ['name' => 'Q4V2',
|
||||
'status' => question_version_status::QUESTION_STATUS_READY, ]);
|
||||
$q4v3 = $questiongenerator->update_question($q4v2, null, ['name' => 'Q4V3',
|
||||
'status' => question_version_status::QUESTION_STATUS_DRAFT]);
|
||||
|
||||
// Test the returned array of questions in that category is the desired one with version three of the first
|
||||
// question, version two of the second question, and the third question omitted completely since there are
|
||||
// only draft versions.
|
||||
$this->assertEquals([$q1v3->id => $q1v3->id, $q2v2->id => $q2v2->id],
|
||||
// question, version two of the second question, the third question omitted completely since there are
|
||||
// only draft versions, and the version two of the fourth question.
|
||||
$this->assertEquals(
|
||||
[$q1v3->id => $q1v3->id, $q2v2->id => $q2v2->id, $q4v2->id => $q4v2->id],
|
||||
question_bank::get_finder()->get_questions_from_categories([$cat->id], ""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,15 +104,14 @@ if ($requestedqtype) {
|
||||
FROM (SELECT qv.id as versionid, qc.contextid, 1 AS numquestions
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid
|
||||
AND qv2.version > qv.version
|
||||
)
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
|
||||
JOIN {context} con ON con.id = qc.contextid
|
||||
$sqlqtypetest
|
||||
AND qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be
|
||||
ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id)
|
||||
AND qv2.questionbankentryid IS NULL
|
||||
AND (q.parent = 0 OR q.parent = q.id)) data
|
||||
GROUP BY data.contextid, data.versionid) result
|
||||
JOIN {context} con ON con.id = result.contextid
|
||||
|
||||
Reference in New Issue
Block a user