Я считаю, что ваше узкое место - это расчеты + ваши очень дорогие внутренние объединения.
Способ, которым вы присоединяетесь, в основном создает перекрестное соединение - он возвращает набор результатов со всеми другими записями, связанными со всеми другими записями, кроме той, для которой указан идентификатор. Затем вы идете и добавляете к этому результирующему набору другие внутренние объединения.
Для каждого внутреннего соединения SQL идет и создает результирующий набор со всеми совпадающими строками.
Таким образом, первое, что вы делаете в своем запросе, это говорите SQL, чтобы он в основном выполнял перекрестное соединение для одной и той же таблицы. (Я предполагаю, что вы все еще следуете, это выглядит довольно продвинуто, поэтому я просто хочу познакомить вас с продвинутым синтаксисом и операторами SQL)
Теперь при следующем внутреннем объединении вы применяете таблицу «Результаты» к вновь созданному огромному набору результатов и только затем отфильтровываете те, которые не обе таблицы.
Итак, для начала, посмотрите, не можете ли вы выполнить свои объединения наоборот. (Это действительно зависит от количества записей в вашей таблице и размеров записей). Попробуйте сначала получить наименьший набор результатов, а затем присоединиться к нему.
Второе, что вы можете попробовать, - это сначала ограничить набор результатов еще до объединения. Так что начните с CTE, где вы фильтруете для o1.id = @trackId. Затем выберите * из этого CTE, выполните объединения в CTE, а затем отфильтруйте в своем запросе o2.id = ISNULL (@nTrackId, o2.id)
Я буду работать на примере, следите за обновлениями ...
-
Хорошо, я добавил пример, сделал быстрый тест, и возвращенные значения совпадают. Проведите это через ваши данные и дайте нам знать, если есть какие-либо улучшения. (Обратите внимание, что это не относится к обсуждаемой точке заказа INNER JOIN, но все же поэкспериментируйте с этим.)
Пример:
ALTER FUNCTION [dbo].[FN_RatingSimilarity_NEW]
(
@trackId INT,
@nTrackId INT,
@measureType VARCHAR(100)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH CTE_ALL AS
(
SELECT id,
name,
releaseDate,
numberOfRatings,
averageRating
FROM dbo.Tracks
WHERE id = @trackId
)
SELECT o2.id,
o2.name,
o2.releaseDate,
o2.numberOfRatings,
o2.averageRating,
COUNT(1) as numberOfSharedUsers,
CASE @measureType
WHEN 'Cosine' THEN SUM(o3.score*o4.score)/(0.01+SQRT(SUM(POWER(o3.score,2))) * SQRT(SUM(POWER(o4.score,2))))
WHEN 'AdjustedCosine' THEN SUM((o3.score-o5.averageRating)*(o4.score-o5.averageRating))/(0.01+SQRT(SUM(POWER(o3.score-o5.averageRating, 2)))*SQRT(SUM(POWER(o4.score-o5.averageRating, 2))))
WHEN 'Pearson' THEN SUM((o3.score-o1.averageRating)*(o4.score-o2.averageRating))/(0.01+SQRT(SUM(POWER(o3.score-o1.averageRating, 2)))*SQRT(SUM(POWER(o4.score-o2.averageRating, 2))))
END as similarityRatio
FROM CTE_ALL o1
INNER JOIN dbo.Tracks o2 ON o2.id != @trackId
INNER JOIN dbo.Ratings o3 ON o3.trackId = o1.id
INNER JOIN dbo.Ratings o4 ON o4.trackId = o2.id AND o4.userId = o3.userId
INNER JOIN dbo.Users o5 ON o5.id = o4.userId
WHERE o2.id = ISNULL(@nTrackId, o2.id)
GROUP BY o2.id,
o2.name,
o2.releaseDate,
o2.numberOfRatings,
o2.averageRating
)