1

J'ai un SQL Server 2008 CTE chargé de renvoyer le Top Review pour un emplacement. Le CTE est enveloppé dans un champ UDF (Table-Valued), et joint sur le champ LocationId, donc je peux obtenir la meilleure évaluation pour chaque emplacement.Aide pour améliorer les performances de SQL Server 2008 CTE

Cardinalités:

Lieu a 0- beaucoup PostLocations
PostLocation a Poster
Post dispose critique

est ici l'UDF:

CREATE FUNCTION [dbo].[Review_HighestRated_Aggregated_ByLocation] 
( 

) 
RETURNS TABLE 
AS 
RETURN 
(
    WITH [RankedLocations] AS 
    (
     SELECT  PL.LocationId, 
        R.Rating, 
        P.PostID, 
        P.UniqueUri, 
        P.Content, 
        ROW_NUMBER() OVER (PARTITION BY PL.LocationId ORDER BY R.Rating DESC, P.LocationTypeId, P.CreatedOn DESC) As ScoreRank 

     From  dbo.PostLocations As PL 
     INNER JOIN dbo.Posts As P 
     ON   P.PostId = PL.PostId 
     INNER JOIN dbo.Reviews As R 
     ON   R.PostId = P.PostId 

     WHERE  R.ReviewTypeId <> 5 
     AND   P.Content IS NOT NULL 
    ) 

    SELECT LocationId, Rating, PostID, UniqueUri, Content 
    FROM RankedLocations 
    WHERE ScoreRank = 1 
) 

Voici un exemple de la façon dont je l'utilise:

select l.LocationId, l.Name, l.UniqueUri, r.UniqueUri, r.Content 
from @Locations l -- temp table containing around 18 location ids 
inner join dbo.Review_HighestRated_Aggregated_ByLocation() r 
on l.LocationId = r.LocationId 

La requête ci-dessus prend 15 secondes à exécuter, ce qui est inacceptable. Sans la jointure à l'UDF cela prend 0 secondes.

Des idées sur comment je peux l'améliorer? Si je regarde le plan d'exécution, c'est le SORT qui prend 98% du coût d'exécution. Le coût IO/sous-arbre de cette opération est ~ 300. J'espérais que le plan d'exécution me donnerait un indice sur un indice que je pourrais créer pour améliorer le coût, mais je n'obtiens rien.

Des idées?

+0

Existe-t-il un index sur 'R.ReviewTypeId' ?? Avez-vous des indices sur les colonnes utilisées pour les relations de clés étrangères ('PostId' dans les trois tableaux)? Aussi: une autre option serait d'essayer d'utiliser directement le CTE dans votre requête, au lieu de le "cacher" à l'intérieur de l'UDF (ce qui peut être notoirement lent ...) - cela fait-il une différence? –

+0

@marc_s. Oui, il existe un index sur ReviewTypeId. Oui, PostId est un PK groupé sur Posts, une partie de PK en cluster sur PostLocations, et PK en cluster sur Reviews. J'ai aussi essayé d'utiliser le CTE directement, pas de changement. J'ai essayé de créer une vue, ce qui est bien, mais vous ne pouvez pas indexer une vue qui contient des opérations agrégées ou des sous-requêtes. En ce moment je penche vers une "table de cache" qui est mise à jour toutes les heures avec un travail planifié de sql. pensées? – RPM1984

Répondre

2

Donc, j'ai trouvé le problème de performance, et ce n'était pas le CTE, c'était comment je l'utilisais.

J'ai plusieurs tables de recherche, une en particulier pour le type de localisation (rue = 7, ville = 5, etc.).

Donc, pour garder mon SQL fluide et cohérente (et éviter les nombres magiques codés en dur), j'ai créé une fonction scalaire wrapper qui retourne la valeur evuivalent en fonction de la chaîne, par exemple:

DECLARE @Street_LocationType = [dbo].[ToLocationTypeId]('Street') 

La fonction est extrêmement simple, juste une série d'instructions CASE.

Mais, j'utilisais mon CTE comme ceci:

SELECT  a.LocationId, b.Content, b.UniqueUri 
FROM  [dbo].[Locations] a 
INNER JOIN dbo.Review_HighestRated_Aggregated_ByLocation() b -- UDF with CTE 
ON   a.LocationId = b.LocationId   
WHERE  a.LocationTypeId = @Street_LocationType 

donc je ne même utiliser sur le CTE lui-même, je l'ai utilisé comme un filtre sur la table Emplacements.

Si je change ce qui précède pour coder en dur la valeur (par exemple 7), le temps d'exécution de la procédure passe de 13 secondes à 2 secondes. Je n'ai pas compris, mais cela a résolu le problème. Je remarquais lorsque la procédure fonctionnait mal, l'opération "TRIER" dans le plan de requête avait un nombre estimé de lignes = 32 000 - ce qui correspond à peu près à tous les messages dans le système.

Après mes modifications, le nombre estimé de lignes est 1 (comme il se doit).

Activité bizarre en effet.

0

Pour convertir le CTE et l'UDF dans une vue:

DROP FUNCTION [dbo].[Review_HighestRated_Aggregated_ByLocation] 
GO 

CREATE VIEW Review_HighestRated_Aggregated_ByLocation 
AS 
SELECT LocationId, Rating, PostID, UniqueUri, Content 
FROM 
(
    SELECT  PL.LocationId, 
       R.Rating, 
       P.PostID, 
       P.UniqueUri, 
       P.Content, 
       ROW_NUMBER() OVER (PARTITION BY PL.LocationId ORDER BY R.Rating DESC, P.LocationTypeId, P.CreatedOn DESC) As ScoreRank 
    From  dbo.PostLocations As PL 
    INNER JOIN dbo.Posts As P 
    ON   P.PostId = PL.PostId 
    INNER JOIN dbo.Reviews As R 
    ON   R.PostId = P.PostId 
    WHERE  R.ReviewTypeId <> 5 
    AND   P.Content IS NOT NULL 
) RankedLocations 
WHERE ScoreRank = 1 

GO 

exemple de requête de OP révisée pour utiliser la nouvelle vue:

select l.LocationId, l.Name, l.UniqueUri, r.UniqueUri, r.Content 
from @Locations l -- temp table containing around 18 location ids 
inner join Review_HighestRated_Aggregated_ByLocation r 
on l.LocationId = r.LocationId 
+0

La conversion de l'UDF en vue vous aidera si vous avez besoin de réutiliser le code dans l'UDF. –

+0

je vais essayer d'affacturage sur le CTE ... – RPM1984

+0

non, fait aucune différence. Je pense que l'idée de pcrofe fonctionnerait si je pouvais trouver comment se débarrasser du CTE et ainsi en faire une vue indexable. – RPM1984

0

Si votre fonction d'une valeur de table ne avez pas besoin de paramètres envisager d'utiliser un VIEW au lieu d'un UDF. Probablement, il résout le problème de performance.

+0

Je vais essayer - bonne idée. En faire une vue ne fait aucune différence. – RPM1984

+0

et vous ne pouvez pas indexer une vue qui a un CTE. – RPM1984

+0

J'ai essayé de remplacer le CTE par un sous-menu, mais vous ne pouvez pas non plus indexer une vue avec un sous-select. des idées de comment je peux écrire cette UDF comme une vue indexable? – RPM1984

Questions connexes