MDL-72533 core_calendar: improve calendar event table peformance

- Filter searchable courses if set.
- Sites with large groups should now experience better performance.
- Applies and optimises query against a proper date range.

Co-authored-by: Mark Sharp <[email protected]>
Co-authored-by: Peter Sistrom <[email protected]>
Co-authored-by: Kevin Pham <[email protected]>
This commit is contained in:
Mark Sharp
2023-03-20 12:15:14 +11:00
committed by Peter Sistrom
co-authored by Peter Sistrom Kevin Pham
parent f3bf17cdfb
commit 4e99fe48bc
@@ -179,6 +179,9 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
$whereclause = implode(' AND ', $whereconditions);
// Build SQL subquery and conditions for filtered events based on priorities.
$subquerytimeconditions = array_filter($whereconditions, function($condition) {
return (strpos($condition, 'time') !== false);
});
$subquerywhere = '';
$subqueryconditions = [];
$subqueryparams = [];
@@ -194,7 +197,12 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
// Set calendar filters.
list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true, $userrecord);
$allusercourses = array_merge($allusercourses, $usercourses);
$filteredcourses = is_array($courses) ? $courses : [$courses];
$filteredcourses = array_filter($usercourses, function($course) use ($filteredcourses) {
return in_array($course, $filteredcourses);
});
$allusercourses = array_merge($allusercourses, $filteredcourses);
// Flag to indicate whether the query needs to exclude group overrides.
$viewgroupsonly = false;
@@ -203,7 +211,8 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
// Set filter condition for the user's events.
// Even though $user is a single scalar, we still use get_in_or_equal() because we are inside a loop.
list($inusers, $inuserparams) = $DB->get_in_or_equal($user, SQL_PARAMS_NAMED);
$subqueryconditions[] = "(ev.userid $inusers AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
$condition = "(ev.userid $inusers AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
$subqueryconditions[] = $condition;
$subqueryparams = array_merge($subqueryparams, $inuserparams);
foreach ($usercourses as $courseid) {
@@ -220,11 +229,11 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
// Set filter condition for the user's group events.
if ($usergroups === true || $viewgroupsonly) {
// Fetch group events, but not group overrides.
$subqueryconditions[] = "(ev.groupid != 0 AND ev.eventtype = 'group')";
$groupconditions = "(ev.groupid != 0 AND ev.eventtype = 'group')";
} else if (!empty($usergroups)) {
// Fetch group events and group overrides.
list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
$subqueryconditions[] = "(ev.groupid $inusergroups)";
$groupconditions = "(ev.groupid $inusergroups)";
$subqueryparams = array_merge($subqueryparams, $inusergroupparams);
}
}
@@ -263,8 +272,20 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
// Set subquery filter condition for the courses.
if (!empty($subquerycourses)) {
list($incourses, $incoursesparams) = $DB->get_in_or_equal($subquerycourses, SQL_PARAMS_NAMED);
$subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid $incourses AND ev.categoryid = 0)";
$subqueryparams = array_merge($subqueryparams, $incoursesparams);
if (isset($groupconditions)) {
$groupconditions = $groupconditions." OR ";
} else {
$groupconditions = '';
}
$condition = "($groupconditions(ev.groupid = 0 AND ev.courseid $incourses AND ev.categoryid = 0))";
$subtimesparams = [];
if (!empty($subquerytimeconditions)) {
$subtimes = $this->subquerytimeconditions("courses", $subquerytimeconditions, $whereparams);
$condition .= $subtimes['where'];
$subtimesparams = $subtimes['params'];
}
$subqueryconditions[] = $condition;
$subqueryparams = array_merge($subqueryparams, $incoursesparams, $subtimesparams);
}
// Set subquery filter condition for the categories.
@@ -272,8 +293,15 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
$subqueryconditions[] = "(ev.categoryid != 0 AND ev.eventtype = 'category')";
} else if (!empty($categories)) {
list($incategories, $incategoriesparams) = $DB->get_in_or_equal($categories, SQL_PARAMS_NAMED);
$subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid = 0 AND ev.categoryid $incategories)";
$subqueryparams = array_merge($subqueryparams, $incategoriesparams);
$condition = "(ev.groupid = 0 AND ev.courseid = 0 AND ev.categoryid $incategories)";
$subtimesparams = [];
if (!empty($subquerytimeconditions)) {
$subtimes = $this->subquerytimeconditions("cats", $subquerytimeconditions, $whereparams);
$condition .= $subtimes['where'];
$subtimesparams = $subtimes['params'];
}
$subqueryconditions[] = $condition;
$subqueryparams = array_merge($subqueryparams, $incategoriesparams, $subtimesparams);
}
// Build the WHERE condition for the sub-query.
@@ -322,4 +350,36 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
return $events === false ? [] : $events;
}
/**
* Returns a query fragment and params, with time constraints applied
*
* @param string $prefix
* @param array $conditions
* @param array $params
* @return array [<where>, <params>]
*/
protected function subquerytimeconditions(string $prefix, array $conditions, array $params): array {
$outwhere = '';
$outparams = [];
// Most specific to least specific.
$timeparams = ['timefromid', 'timefrom3', 'timefrom2', 'timefrom1', 'timefrom', 'timetoid', 'timeto2', 'timeto1', 'timeto'];
$whereconditions = [];
foreach ($conditions as $condition) {
$where = $condition;
// This query has been borrowed from the main WHERE clause, so the alias needs to be renamed to match the union.
$where = str_replace('e.id', 'ev.id', $where);
foreach ($timeparams as $timeparam) {
if (isset($params[$timeparam])) {
$where = str_replace(":{$timeparam}", ":{$prefix}{$timeparam}", $where);
$outparams["{$prefix}{$timeparam}"] = $params[$timeparam];
}
}
$whereconditions[] = $where;
}
if (count($whereconditions) > 0) {
$outwhere = ' AND ' . implode(' AND ', $whereconditions);
}
return ['where' => $outwhere, 'params' => $outparams];
}
}