Jak dołączyć do tabeli z funkcją wycenioną w tabeli?

53

Mam funkcję zdefiniowaną przez użytkownika:

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

Teraz chcę dołączyć do tego na innym stole, na przykład:

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

Innymi słowy, dla wszystkich Foorekordów, w których SomeCriterionjest 1, chcę zobaczyć Foo IDi Descobok wartości Field1i, Field2które są zwracane ut_FooFuncdla danych wejściowych Foo.ID.

Jaka jest składnia, aby to zrobić?

Shaul Behr
źródło

Odpowiedzi:

84

Nie musisz CROSS APPLYdołączać.

Definicja wyrażeń tabelowych związanych z łączeniami musi być stabilna. Tzn. Nie można ich skorelować w taki sposób, że wyrażenie tabeli oznacza coś innego, zależnie od wartości wiersza w innej tabeli.

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
źródło
0

Wiem, że wątek jest stary, zadano mi to samo pytanie, zrobiłem test, wynik jest następujący ...

Rekordy w FacCurrencyRate = 14264, podczas gdy TestFunction zwraca 105, jeśli zostanie wykonane niezależnie.

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

Czas wykonania wynosi ...

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

Jeśli użyję sugerowanej odpowiedzi w następujący sposób ...

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

Czas wykonania i liczba wyników to ...

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

Widzę tutaj, że wewnętrzne zapytanie zapewnia bardziej poprawny zestaw wyników, a czas wykonania jest znacznie bardziej wydajny. Popraw mnie z lepszym podejściem do osiągnięcia tego samego!

użytkownik3104116
źródło
1
Zastosuj krzyżyk jest stosowany do każdego zewnętrznego wiersza (więc rząd po rzędzie), dlatego jest o wiele wolniejszy, szczególnie w przypadku większych zestawów danych. Odwracając go (ponieważ chcesz tylko wyników, które pasują do TVF, to znacznie skracasz czas egzekucji (brak połączeń, które nic nie robią), a także liczbę zwracanych wierszy. Ale twoje dwa stwierdzenia nie są równoważne jak drugie zwraca znacznie więcej wierszy niż pierwszy. Jeśli chodzi o poprawność, zależy to od wymagań biznesowych
Jonathan Fite
Tak, zgadzam się. Napisałem jednak kod w świetle wstępnego pytania, w którym założyłem, że między tabelami istnieje relacja jeden do wielu. Po drugie, w wywiadzie zadano mi pytanie. Dzięki
user3104116