Comment rejoindre une table avec une fonction table value?

53

J'ai une fonction définie par l'utilisateur:

create function ut_FooFunc(@fooID bigint, @anotherParam tinyint)
returns @tbl Table (Field1 int, Field2 varchar(100))
as
begin
  -- blah blah
end

Maintenant, je veux rejoindre ceci sur une autre table, comme ceci:

select f.ID, f.Desc, u.Field1, u.Field2
from Foo f 
join ut_FooFunc(f.ID, 1) u -- doesn't work
where f.SomeCriterion = 1

En d’autres termes, pour tous les Fooenregistrements où SomeCriterionest égal à 1, je souhaite afficher les valeurs Foo IDet Desc, ainsi que les valeurs de Field1et Field2qui sont renvoyées ut_FooFuncpour une entrée de Foo.ID.

Quelle est la syntaxe pour faire cela?

Shaul Behr
la source

Réponses:

84

Vous n'avez CROSS APPLYpas besoin de rejoindre.

La définition des expressions de table impliquées dans les jointures doit être stable. C'est-à-dire qu'ils ne peuvent pas être corrélés de telle sorte que l'expression de table signifie quelque chose de différent en fonction de la valeur d'une ligne dans une autre table.

select f.ID, f.Desc, u.Field1, u.Field2
from Foo f 
Cross apply ut_FooFunc(f.ID, 1) u
where f.SomeCriterion = ...
Martin Smith
la source
0

Je sais que le fil est vieux, on m'a posé la même question, j'ai fait un test, le résultat est comme suit ...

Enregistrements dans FacCurrencyRate = 14264 pendant que TestFunction renvoie 105 si exécuté indépendamment.

    SELECT F.*, x.CurrencyKey, x.CurrencyName
    FROM ( 
           SELECT CurrencyKey, CurrencyName FROM dbo.TestFunction()
        ) x
    INNER JOIN [dbo].[FactCurrencyRate] F ON x.CurrencyKey = f.CurrencyKey;

Le temps d'exécution est ...

    (14264 rows affected)
    Table 'FactCurrencyRate'. Scan count 1, logical reads 75, physical reads 1, read-ahead reads 73, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'DimCurrency'. Scan count 1, logical reads 2, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 31 ms,  elapsed time = 749 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

Si j'utilise la réponse suggérée comme suit ...

select F.*, x.CurrencyKey, x.CurrencyName from [dbo].[FactCurrencyRate] F
cross apply dbo.TestFunction() x

Le temps d'exécution et le nombre de résultats sont ...

(1497720 rows affected)
Table 'FactCurrencyRate'. Scan count 1, logical reads 75, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 38110, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'DimCurrency'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 2106 ms,  elapsed time = 43242 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

Ce que je vois ici, c'est que la requête interne produit un ensemble de résultats plus correct et que le temps d'exécution est beaucoup plus efficace. Corrigez-moi avec une meilleure approche pour accomplir la même chose!

utilisateur3104116
la source
1
L'application en croix est appliquée à chaque ligne externe (donc ligne par ligne), c'est pourquoi elle est beaucoup plus lente, en particulier pour les grands ensembles de données. En l'inversant (parce que vous voulez uniquement des résultats qui ont une correspondance dans la TVF, vous réduisez considérablement le temps d'exécution (aucun appel ne fait rien), ainsi que le nombre de lignes renvoyées. Mais vos deux instructions ne sont pas équivalentes à la seconde. renvoie beaucoup plus de lignes que la première. En ce qui concerne l'exactitude, cela dépend des besoins de votre entreprise.
Jonathan Fite
Oui je suis d'accord. Cependant, j’ai écrit le code à la lumière de la question initiale, où j’ai supposé qu’il existait une relation de un à plusieurs entre les tables. Et, deuxièmement, on m'a posé une question lors d'une interview. Merci
user3104116 le