PERF: improve performance of category topic list

In some cases CTE caused pathologically bad query plans.
This optimises it so query runs by itself and caches for lifetime
of the topic query object.

This lightweight caching is done cause topic query will often
execute two queries (one for pinned and one for non pinned)
This commit is contained in:
Sam Saffron
2020-02-29 15:40:54 +11:00
parent 18209e1daf
commit b4999acadd

View File

@@ -669,20 +669,11 @@ class TopicQuery
if options[:no_subcategories]
result = result.where('categories.id = ?', category_id)
else
sql = <<~SQL
categories.id IN (
WITH RECURSIVE subcategories AS (
SELECT :category_id id, 1 depth
UNION
SELECT categories.id, (subcategories.depth + 1) depth
FROM categories
JOIN subcategories ON subcategories.id = categories.parent_category_id
WHERE subcategories.depth < :max_category_nesting
)
SELECT subcategories.id FROM subcategories
) AND (categories.id = :category_id OR topics.id != categories.topic_id)
result = result.where(<<~SQL, subcategory_ids: subcategory_ids(category_id), category_id: category_id)
categories.id in (:subcategory_ids) AND (
categories.topic_id <> topics.id OR categories.id = :category_id
)
SQL
result = result.where(sql, category_id: category_id, max_category_nesting: SiteSetting.max_category_nesting)
end
result = result.references(:categories)
@@ -1061,6 +1052,29 @@ class TopicQuery
private
def subcategory_ids(category_id)
@subcategory_ids ||= {}
@subcategory_ids[category_id] ||=
begin
sql = <<~SQL
WITH RECURSIVE subcategories AS (
SELECT :category_id id, 1 depth
UNION
SELECT categories.id, (subcategories.depth + 1) depth
FROM categories
JOIN subcategories ON subcategories.id = categories.parent_category_id
WHERE subcategories.depth < :max_category_nesting
)
SELECT id FROM subcategories
SQL
DB.query_single(
sql,
category_id: category_id,
max_category_nesting: SiteSetting.max_category_nesting
)
end
end
def sanitize_sql_array(input)
ActiveRecord::Base.public_send(:sanitize_sql_array, input.join(','))
end