Bagaimana cara bergabung dengan tabel dengan fungsi bernilai tabel?

53

Saya memiliki fungsi yang ditentukan pengguna:

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

Sekarang saya ingin bergabung dengan ini di meja lain, seperti:

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

Dengan kata lain, untuk semua Foocatatan di mana SomeCriterion1, saya ingin melihat Foo IDdan Desc, di samping nilai Field1dan Field2yang dikembalikan dari ut_FooFuncuntuk input Foo.ID.

Apa sintaks untuk melakukan ini?

Shaul Behr
sumber

Jawaban:

84

Anda CROSS APPLYtidak perlu bergabung.

Definisi ekspresi tabel yang terlibat dalam gabungan harus stabil. Yaitu Mereka tidak dapat dikorelasikan sehingga ekspresi tabel berarti sesuatu yang berbeda bergantung pada nilai baris di tabel lain.

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

Saya tahu utasnya sudah tua, saya ditanya pertanyaan yang sama, saya melakukan tes, hasilnya sebagai berikut ...

Catatan dalam FacCurrencyRate = 14264 sementara TestFunction mengembalikan 105 jika dieksekusi secara independen.

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

Waktu eksekusi adalah ...

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

Jika saya menggunakan jawaban yang disarankan sebagai berikut ...

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

Waktu eksekusi dan jumlah hasil adalah ...

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

Apa yang saya lihat di sini adalah bahwa kueri dalam menghasilkan set hasil yang lebih benar dan waktu eksekusi jauh lebih efisien. Perbaiki saya dengan pendekatan yang lebih baik untuk mencapai hal yang sama!

pengguna3104116
sumber
1
Silang berlaku diterapkan pada setiap baris luar (jadi baris demi baris), inilah mengapa jauh lebih lambat, terutama pada dataset yang lebih besar. Dengan membalikkannya (karena Anda hanya ingin hasil yang memiliki kecocokan di TVF maka Anda sangat mengurangi waktu exectuion (tidak ada panggilan yang tidak melakukan apa-apa) serta berapa banyak baris yang Anda kembalikan. Tetapi dua pernyataan Anda tidak setara dengan yang kedua mengembalikan lebih banyak baris daripada yang pertama. Sejauh kebenarannya, itu tergantung pada kebutuhan bisnis Anda
Jonathan Fite
Ya saya setuju. Namun, saya menulis kode mengingat pertanyaan awal di mana saya berasumsi bahwa ada hubungan satu ke banyak antara tabel. Dan, kedua, saya ditanyai dalam sebuah wawancara. Terima kasih
user3104116