3

J'ai un UDF en ligne, évalué par table. Je veux filtrer les résultats de cette UDF pour obtenir une valeur particulière. Quand je spécifie le filtre en utilisant un paramètre constant, tout est génial et la performance est presque instantanée. Lorsque je spécifie le filtre à l'aide d'un paramètre variable, cela prend beaucoup plus de temps, de l'ordre de 500 lectures plus logiques et 20 fois plus longues. Le plan d'exécution montre que dans le cas de paramètre variable, le filtre n'est appliqué que très tard dans le processus, provoquant plusieurs analyses d'index plutôt que les recherches effectuées dans le cas constant. Je suppose que mes questions sont les suivantes: Pourquoi, étant donné que je spécifie un seul paramètre de filtre qui va être très sélectif par rapport à un champ indexé, mes performances vont-elles dans les mauvaises herbes quand ce paramètre est dans une variable? Y a-t-il quelque chose que je puisse faire à ce sujet?Pourquoi la fonction UDF de ma table en ligne est-elle beaucoup plus lente lorsque j'utilise des paramètres variables plutôt que des paramètres constants?

Cela a-t-il quelque chose à voir avec la fonction analytique dans la requête?

Voici mes questions:

CREATE FUNCTION fn_test() 
RETURNS TABLE 
WITH SCHEMABINDING 
AS 
    RETURN 
    SELECT DISTINCT GCN_SEQNO, Drug_package_version_ID 
    FROM 
    (
     SELECT COALESCE(ndctbla.GCN_SEQNO, ndctblb.GCN_SEQNO) AS GCN_SEQNO, 
      dpv.Drug_package_version_ID, ROW_NUMBER() OVER (PARTITION BY dpv.Drug_package_version_id ORDER BY 
       ndctbla.GCN_SEQNO DESC) AS Predicate 
     FROM dbo.Drug_Package_Version dpv 
      LEFT JOIN dbo.NDC ndctbla ON ndctbla.NDC = dpv.Sp_package_code 
      LEFT JOIN dbo.NDC ndctblb ON ndctblb.SPC_NDC = dpv.Sp_package_code 
    ) iq 
    WHERE Predicate = 1 
GO 

GRANT SELECT ON fn_test TO public 
GO 

-- very fast 
SELECT GCN_SEQNO 
FROM dbo.fn_test() 
WHERE Drug_package_version_id = 10000 

GO 

-- comparatively slow 
DECLARE @dpvid int 
SET @dpvid = 10000 
SELECT GCN_SEQNO 
FROM dbo.fn_test() 
WHERE Drug_package_version_id = @dpvid 
+0

Pouvez-vous publier les deux plans d'exécution quelque part afin que je puisse voir exactement ce que fait SQL Server dans les deux cas? – mrdenny

Répondre

1

Les réponses que j'ai reçues étaient bonnes, et j'ai appris d'elles, mais je pense avoir trouvé une réponse qui me satisfait.

Je pense que c'est l'utilisation de la clause PARTITION BY qui cause le problème ici. Je reformulé l'UDF en utilisant une variante de l'idiome autojointure:

SELECT t1.A, t1.B, t1.C 
FROM T t1 
    INNER JOIN 
    (
     SELECT A, MAX(C) AS C 
     FROM T 
     GROUP BY A 
    ) t2 ON t1.A = t2.A AND t1.C = t2.C 

Ironie du sort, ce qui est plus performant que d'utiliser la requête SQL spécifique à 2008, ainsi que l'optimiseur ne dispose pas d'un problème se joindre à cette version de la requête en utilisant des variables plutôt que des constantes. À ce stade, je conclus que l'optimiseur ne gère pas les extensions SQL les plus récentes ainsi que les anciennes. En prime, je peux utiliser l'UDF maintenant, dans mes plates-formes SQL 2000 pré-mises à jour.

Merci pour votre aide, tout le monde!

+0

Il n'y a aucune raison de penser que les nouvelles fonctionnalités du langage de requête se traduiront par de meilleures performances. Ils ont passé beaucoup plus de temps à régler les expressions classiques. – dkretz

2

Une fois que vous créez une nouvelle projection à travers une UDF, il ne peut pas attendre à ce que vos index restent applicables sur les colonnes qui sont indexées sur la table d'origine et inclus dans la projection. Lorsque vous filtrez sur la projection (et non dans le fichier UDF par rapport à la table d'origine avec les index), les index ne s'appliquent plus.

Ce que vous voulez faire est de paramétrer la fonction à prendre dans le paramètre. Si vous constatez que vous avez trop de champs sur lesquels vous souhaitez définir des paramètres, vous pouvez consulter les vues indexées, car vous pouvez créer votre projection et l'indexer, puis lancer des requêtes sur cette vue. .

+0

Quelques points - si mes index ne s'appliquaient pas, cela ne causerait-il pas de mauvaises performances dans les deux cas? En outre, la vue indexée est une bonne idée mais la fonction de classement exclut la requête pour le moment. – mwigdahl

+0

@mwigdahl: J'ai mis à jour mon message pour indiquer pourquoi les index ne s'appliqueront pas lorsque vous filtrez sur la projection (par opposition à directement sur la table). En outre, pouvez-vous créer un UDF qui utilise la fonction de classement, puis l'appeler à partir de la vue indexée? Puis créez la requête qui filtre sur la vue indexée? – casperOne

1

Simplement, la constante est facile à évaluer dans le plan. La variable locale n'est pas. En particulier avec la fonction de classement et le filtre Predicate = 1

Paraphrasant casparOne, vous devez pousser le filtre aussi loin vers l'intérieur que possible afin de filtrer dpv.Drug_package_version_id à l'intérieur de la table dérivée iq.

Si vous faites cela, alors vous n'avez pas non plus besoin du PARTITION BY parce que vous n'avez qu'un seul dpv.Drug_package_version_id. Ensuite, vous pouvez faire un nettoyeur ...TOP 1 ... ORDER BY ndctbla.GCN_SEQNO DESC.