From 25a487b90a2653f6facef41d7ec27a6cc0e23eae Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 25 Sep 2014 15:52:00 +0100 Subject: [PATCH] MDL-47426 assign local roles: rewrite query for better performance. This is an extremely dangerous query, because it includes the user table twice, along-side two other potentially large tables, role_assignments and user_enrolments. The solution is to rewrite the query so that: 1. The subquery is JOINed, not WHERE ... INed. Typically query optimisers handle the JOIN case better. 2. Before the join was role-assignments <-> users <-> subquery. That is, everything was linked to u.id. Now the linking is role-assignments <-> subquery <-> users, so the SELECT DISTINT eu1_u.id FROM {enrolled users} is central. That seems to send a strong hint to the query optimiser about a good order to execute the query. --- .../classes/potential_assignees_below_course.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/admin/roles/classes/potential_assignees_below_course.php b/admin/roles/classes/potential_assignees_below_course.php index 71664a19897..ce603663995 100644 --- a/admin/roles/classes/potential_assignees_below_course.php +++ b/admin/roles/classes/potential_assignees_below_course.php @@ -48,11 +48,12 @@ class core_role_potential_assignees_below_course extends core_role_assign_user_s $fields = 'SELECT ' . $this->required_fields_sql('u'); $countfields = 'SELECT COUNT(u.id)'; - $sql = " FROM {user} u - LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.roleid = :roleid AND ra.contextid = :contextid) - WHERE u.id IN ($enrolsql) - $wherecondition - AND ra.id IS NULL"; + $sql = " FROM ($enrolsql) enrolled_users_view + JOIN {user} u ON u.id = enrolled_users_view.id + LEFT JOIN {role_assignments} ra ON (ra.userid = enrolled_users_view.id AND + ra.roleid = :roleid AND ra.contextid = :contextid) + WHERE ra.id IS NULL + $wherecondition"; $params['contextid'] = $this->context->id; $params['roleid'] = $this->roleid;