From 4fbe8bec4a70236ef116faa79507629bb2c5ca39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Thu, 7 May 2020 21:06:28 +0200 Subject: [PATCH] MDL-68647 forum: Improve the SQL performance in the privacy provider The idea here is to replace the existing LEFT JOINs and OR / IS NOT NULL conditions with INNER JOINs and ANDs. So we gather the discussions data from all three areas in individual queries, make their UNION. The GROUP BY was present before and maybe it is not even needed any more (due to how UNION works) but it should not hurt to keep it. This leads to significantly improved performance. --- mod/forum/classes/privacy/provider.php | 59 +++++++++++++++----------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/mod/forum/classes/privacy/provider.php b/mod/forum/classes/privacy/provider.php index 49da231aafd..a7c96873cad 100644 --- a/mod/forum/classes/privacy/provider.php +++ b/mod/forum/classes/privacy/provider.php @@ -692,35 +692,44 @@ class provider implements protected static function export_all_posts(int $userid, array $mappings) { global $DB; - // Find all of the posts, and post subscriptions for this forum. - list($foruminsql, $forumparams) = $DB->get_in_or_equal(array_keys($mappings), SQL_PARAMS_NAMED); - $ratingsql = \core_rating\privacy\provider::get_sql_join('rat', 'mod_forum', 'post', 'p.id', $userid); - $sql = "SELECT - p.discussion AS id, - f.id AS forumid, - d.name, - d.groupid - FROM {forum} f - JOIN {forum_discussions} d ON d.forum = f.id - JOIN {forum_posts} p ON p.discussion = d.id - LEFT JOIN {forum_read} fr ON fr.postid = p.id AND fr.userid = :readuserid - {$ratingsql->join} - WHERE f.id ${foruminsql} AND - ( - p.userid = :postuserid OR - p.privatereplyto = :privatereplyrecipient OR - fr.id IS NOT NULL OR - {$ratingsql->userwhere} - ) - GROUP BY f.id, p.discussion, d.name, d.groupid - "; + $commonsql = "SELECT p.discussion AS id, f.id AS forumid, d.name, d.groupid + FROM {forum} f + JOIN {forum_discussions} d ON d.forum = f.id + JOIN {forum_posts} p ON p.discussion = d.id"; + + // All discussions with posts authored by the user or containing private replies to the user. + list($foruminsql1, $forumparams1) = $DB->get_in_or_equal(array_keys($mappings), SQL_PARAMS_NAMED); + $sql1 = "{$commonsql} + WHERE f.id {$foruminsql1} + AND (p.userid = :postuserid OR p.privatereplyto = :privatereplyrecipient)"; + + // All discussions with the posts marked as read by the user. + list($foruminsql2, $forumparams2) = $DB->get_in_or_equal(array_keys($mappings), SQL_PARAMS_NAMED); + $sql2 = "{$commonsql} + JOIN {forum_read} fr ON fr.postid = p.id + WHERE f.id {$foruminsql2} + AND fr.userid = :readuserid"; + + // All discussions with ratings provided by the user. + list($foruminsql3, $forumparams3) = $DB->get_in_or_equal(array_keys($mappings), SQL_PARAMS_NAMED); + $ratingsql = \core_rating\privacy\provider::get_sql_join('rat', 'mod_forum', 'post', 'p.id', $userid, true); + $sql3 = "{$commonsql} + {$ratingsql->join} + WHERE f.id {$foruminsql3} + AND {$ratingsql->userwhere}"; + + $sql = "SELECT * + FROM ({$sql1} UNION {$sql2} UNION {$sql3}) united + GROUP BY id, forumid, name, groupid"; $params = [ - 'postuserid' => $userid, - 'readuserid' => $userid, + 'postuserid' => $userid, + 'readuserid' => $userid, 'privatereplyrecipient' => $userid, ]; - $params += $forumparams; + $params += $forumparams1; + $params += $forumparams2; + $params += $forumparams3; $params += $ratingsql->params; $discussions = $DB->get_records_sql($sql, $params);