Wie verbinde ich eine Tabelle mit einer Tabellenwertfunktion?

53

Ich habe eine benutzerdefinierte Funktion:

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

Jetzt möchte ich dies auf einem anderen Tisch verbinden, wie folgt:

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

Mit anderen Worten, für alle FooDatensätze, bei denen SomeCriterion1 ist, möchte ich das Foo IDund Descneben den Werten von Field1und sehen Field2, die ut_FooFuncfür eine Eingabe von zurückgegeben werden Foo.ID.

Was ist die Syntax, um dies zu tun?

Shaul Behr
quelle

Antworten:

84

Du musst CROSS APPLYnicht mitmachen.

Die Definition von Tabellenausdrücken in Joins muss stabil sein. Dh Sie können nicht so korreliert werden, dass der Tabellenausdruck abhängig vom Wert einer Zeile in einer anderen Tabelle etwas anderes bedeutet.

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
quelle
0

Ich weiß, der Thread ist alt, mir wurde die gleiche Frage gestellt, ich habe einen Test durchgeführt, das Ergebnis ist wie folgt ...

Zeichnet in FacCurrencyRate = 14264 auf, während TestFunction 105 zurückgibt, wenn es unabhängig ausgeführt wird.

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

Die Ausführungszeit beträgt ...

    (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.

Wenn ich die vorgeschlagene Antwort wie folgt verwende ...

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

Die Ausführungszeit und die Anzahl der Ergebnisse ist ...

(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.

Was ich hier sehe, ist, dass die innere Abfrage korrektere Ergebnisse liefert und die Ausführungszeit viel effizienter ist. Korrigieren Sie mich mit einem besseren Ansatz, um dasselbe zu erreichen!

user3104116
quelle
1
Das Anwenden von Kreuz wird auf jede äußere Zeile angewendet (also Zeile für Zeile). Aus diesem Grund ist es, insbesondere bei größeren Datensätzen, sehr viel langsamer. Indem Sie es umkehren (weil Sie nur Ergebnisse wünschen, die mit dem TVF übereinstimmen, reduzieren Sie die Ausführungszeit (keine Aufrufe, die nichts bewirken) und die Anzahl der zurückgegebenen Zeilen erheblich. Aber Ihre beiden Anweisungen sind nicht gleichbedeutend mit der zweiten Gibt weit mehr Zeilen als die erste zurück. Was die Richtigkeit betrifft, hängt dies von Ihren geschäftlichen Anforderungen ab.
Jonathan Fite,
Ja, ich stimme zu. Ich habe den Code jedoch in Anbetracht der anfänglichen Frage geschrieben, in der ich angenommen habe, dass es eine Eins-zu-viele-Beziehung zwischen den Tabellen gibt. Und zweitens wurde mir in einem Interview eine Frage gestellt. Vielen Dank
user3104116