SQL Server, табличные функции, медленная обработка - PullRequest
0 голосов
/ 19 декабря 2011

Я перепробовал все, но не смог преодолеть эту проблему.

У меня есть табличная функция.

Когда я вызываю эту функцию с помощью

SELECT * FROM Ratings o1 
    CROSS APPLY dbo.FN_RatingSimilarity(50, 497664, 'Cosine') o2 
WHERE o1.trackId = 497664

Требуется время для выполнения.Но когда я это сделаю.

SELECT * FROM Ratings o1 
    CROSS APPLY dbo.FN_RatingSimilarity(50, o1.trackId, 'Cosine') o2 
WHERE o1.trackId = 497664

Выполняется за 32 секунды.Я создал все индексы, но это не помогло.

Кстати, моя функция:

ALTER FUNCTION [dbo].[FN_RatingSimilarity]
(   
    @trackId    INT,
    @nTrackId   INT,
    @measureType    VARCHAR(100)
)
RETURNS TABLE 
WITH SCHEMABINDING
AS
    RETURN
    (
        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 dbo.Tracks 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 o1.id = @trackId 
             AND o2.id = ISNULL(@nTrackId, o2.id)
      GROUP BY o2.id, 
               o2.name, 
               o2.releaseDate,
               o2.numberOfRatings, 
               o2.averageRating
    )

Любая помощь будет оценена.

Спасибо.Эмр

1 Ответ

1 голос
/ 19 декабря 2011

Я считаю, что ваше узкое место - это расчеты + ваши очень дорогие внутренние объединения.

Способ, которым вы присоединяетесь, в основном создает перекрестное соединение - он возвращает набор результатов со всеми другими записями, связанными со всеми другими записями, кроме той, для которой указан идентификатор. Затем вы идете и добавляете к этому результирующему набору другие внутренние объединения.

Для каждого внутреннего соединения 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 
    ) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...