web-dev-qa-db-fra.com

CROSS APPLY avec performance de restriction de fonction à valeur table

J'ai un problème avec CROSS APPLY avec la fonction de valeur de table paramétrée. Voici un exemple de pseudo-code simplifié:

SELECT * 
FROM (
    SELECT lor.*
    FROM LOT_OF_ROWS_TABLE lor
    WHERE ...
) AS lor
CROSS APPLY dbo.HeavyTableValuedFunction(lor.ID) AS htvf
INNER JOIN ANOTHER_TABLE AS at ON lor.ID = at.ID 
WHERE ...
  • Sélection interne sur la table LOT_OF_ROWS_TABLE renvoie plusieurs lignes.
  • La jonction des tables LOT_OF_ROWS_TABLE et ANOTHER_TABLE ne renvoie qu'une ou plusieurs lignes. 
  • La fonction table value prend beaucoup de temps et lorsque vous appelez pour un grand nombre de lignes , La sélection dure très longtemps.

Mon problème:  

La fonction est appelée pour toutes les lignes renvoyées par LOT_OF_ROWS_TABLE, indépendamment du fait que les données seront limitées lorsque vous joindrez simplement ANOTHER_TABLE.

La sélection doit être dans le format indiqué - elle est générée et en fait, elle est beaucoup plus difficile.

Quand j'essaye de le réécrire, ça peut être très rapide, mais ça ne peut pas être réécrit comme ça:

SELECT * 
FROM (
    SELECT lor.*
    FROM LOT_OF_ROWS_TABLE lor
    WHERE ...
) AS lor
INNER JOIN ANOTHER_TABLE AS at ON lor.ID = at.ID 
CROSS APPLY dbo.HeavyTableValuedFunction(at.ID) AS htvf
WHERE ...

J'aimerais savoir:

Existe-t-il un paramètre ou un indice ou quelque chose qui oblige select à appeler la fonction uniquement pour les lignes finalement restreintes?

Je vous remercie.

EDIT:

La fonction table value est très complexe: http://Pastebin.com/w6azRvxR . La sélection dont nous parlons est "configurée par l'utilisateur" et générée: http://Pastebin.com/bFbanY2n .

8
Pavel Hodek

vous pouvez diviser cette requête en 2 parties utilisent soit une variable de table, soit une table temporaire 

SELECT lor.*,at.* into #tempresult
FROM (
    SELECT lor.*
    FROM LOT_OF_ROWS_TABLE lor
    WHERE ...
) lor
INNER JOIN ANOTHER_TABLE AS at ON lor.ID = at.ID 
WHERE ...

maintenant faire la partie qui prend du temps qui est la fonction de valeur de table droite

SELECT  * FROM #tempresult
CROSS APPLY dbo.HeavyTableValuedFunction(#tempresult.ID) AS htvf
2
Sankara

Je crois que c'est ce que vous recherchez. 

Scénario de forçage de plan: création d'un guide de plan pour forcer un plan obtenu à partir d'une requête réécrite

En gros, il décrit la réécriture de la requête pour obtenir un plan généré en utilisant le bon ordre de jointure. Enregistrez ensuite ce plan et forcez votre requête existante (qui ne sera pas modifiée) à utiliser le plan que vous avez enregistré.

Le lien BOL que je mets donne même un exemple spécifique de réécriture de la requête en plaçant les jointures dans un ordre différent et en utilisant un indice FORCE ORDER. Ensuite, utilisez sp_create_plan_guild pour extraire le plan de la requête réécrite et l'utiliser pour la requête d'origine.

1
Kenneth Fisher

OUI et NON ... il est difficile d'interpréter ce que vous essayez d'accomplir sans échantillon de données IN et result OUT, pour comparer les résultats.

J'aimerais savoir:

Existe-t-il un paramètre ou un indice ou quelque chose qui oblige la sélection à appeler la fonction Uniquement pour les lignes finalement restreintes?

Je vais donc répondre à votre question ci-dessus (3 ans plus tard !!) directement, avec une déclaration directe:

Vous devez vous renseigner sur le CTE et sur la différence entre CROSS APPLY Et INNER JOIN, et pourquoi utiliser CROSS APPLY dans votre cas est Nécessaire. Vous "pourriez" prendre le code dans votre fonction et l'appliquer Dans une seule instruction SQL utilisant CTE.

c'est à dire:

Lire ceci et ceci .

Essentiellement, quelque chose comme ça ...

WITH    t2o AS
        (
        SELECT  t2.*, ROW_NUMBER() OVER (PARTITION BY t1_id ORDER BY rank) AS rn
        FROM    t2
        )
SELECT  t1.*, t2o.*
FROM    t1
INNER JOIN
        t2o
ON      t2o.t1_id = t1.id
        AND t2o.rn <= 3

Appliquez votre requête pour extrapoler la date souhaitée ONCE, puis utilisez CTE, puis appliquez votre deuxième SQL à l’aide de CROSS APPLY.

Tu n'as pas le choix. Vous ne pouvez pas faire ce que vous essayez de faire dans ONE SQL.

0
Fandango68