Apakah SQL Server cache hasil dari fungsi multi-pernyataan tabel-nilai?

22

Fungsi bernilai tabel multi-pernyataan mengembalikan hasilnya dalam variabel tabel.

Apakah hasil ini pernah digunakan kembali, atau apakah fungsi selalu sepenuhnya dievaluasi setiap kali dipanggil?

Paul White mengatakan GoFundMonica
sumber

Jawaban:

23

Hasil dari fungsi bernilai tabel multi-pernyataan (msTVF) tidak pernah cache atau digunakan kembali di seluruh pernyataan (atau koneksi), tetapi ada beberapa cara bahwa hasil msTVF dapat digunakan kembali dalam pernyataan yang sama. Sejauh itu, sebuah msTVF tidak perlu dihuni kembali setiap kali disebut.

Contoh msTVF

MsTVF (sengaja tidak efisien) ini mengembalikan rentang integer yang ditentukan, dengan cap waktu di setiap baris:

IF OBJECT_ID(N'dbo.IntegerRange', 'TF') IS NOT NULL
    DROP FUNCTION dbo.IntegerRange;
GO
CREATE FUNCTION dbo.IntegerRange (@From integer, @To integer)
RETURNS @T table 
(
    n integer PRIMARY KEY, 
    ts datetime DEFAULT CURRENT_TIMESTAMP
)
WITH SCHEMABINDING
AS
BEGIN
    WHILE @From <= @To
    BEGIN
        INSERT @T (n)
        VALUES (@From);

        SET @From = @From + 1;
    END;
    RETURN;
END;

Variabel tabel statis

Jika semua parameter untuk panggilan fungsi adalah konstanta (atau konstanta runtime), rencana eksekusi akan mengisi hasil variabel tabel sekali. Sisa dari rencana dapat mengakses variabel tabel berkali-kali. Sifat statis dari variabel tabel dapat dikenali dari rencana eksekusi. Sebagai contoh:

SELECT
    IR.n,
    IR.ts 
FROM dbo.IntegerRange(1, 5) AS IR
ORDER BY
    IR.n;

Mengembalikan hasil yang mirip dengan:

Hasil sederhana

Rencana pelaksanaannya adalah:

Rencana eksekusi sederhana

Operator Sequence pertama-tama memanggil operator Table Valued Function, yang mengisi variabel tabel (perhatikan operator ini tidak mengembalikan baris). Selanjutnya, Sequence memanggil input kedua, yang mengembalikan konten variabel tabel (menggunakan Clustered Index Scan dalam kasus ini).

Hadiah bahwa rencana tersebut menggunakan hasil tabel variabel 'statis' adalah operator Fungsi Bernilai Tabel di bawah Urutan - variabel tabel perlu diisi penuh satu kali sebelum sisa rencana dapat berjalan.

Akses ganda

Untuk menunjukkan hasil variabel tabel yang diakses lebih dari sekali, kami akan menggunakan tabel kedua dengan baris bernomor 1 hingga 5:

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
    DROP TABLE dbo.T;

CREATE TABLE dbo.T (i integer NOT NULL);

INSERT dbo.T (i) 
VALUES (1), (2), (3), (4), (5);

Dan permintaan baru yang bergabung tabel ini untuk fungsi kita (ini bisa sama-sama ditulis sebagai APPLY):

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
JOIN dbo.IntegerRange(1, 5) AS IR
    ON IR.n = T.i;

Hasilnya adalah:

Bergabunglah hasilnya

Rencana pelaksanaan:

Bergabunglah dengan rencana

Seperti sebelumnya, Sequence mempopulasikan hasil tabel variabel msTVF terlebih dahulu. Selanjutnya, loop bersarang digunakan untuk bergabung dengan setiap baris dari tabelT ke baris dari hasil msTVF. Karena definisi fungsi menyertakan indeks bermanfaat pada variabel tabel, pencarian indeks dapat digunakan.

Poin kuncinya adalah ketika parameter ke msTVF adalah konstanta (termasuk variabel & parameter) atau diperlakukan sebagai konstanta runtime untuk pernyataan oleh mesin eksekusi, paket tersebut akan menampilkan dua operator terpisah untuk hasil variabel tabel msTVF: satu untuk mengisi meja; lain untuk mengakses hasil, mungkin mengakses tabel beberapa kali, dan mungkin menggunakan indeks yang dinyatakan dalam definisi fungsi.

Parameter terkait dan parameter tidak konstan

Untuk menyorot perbedaan ketika parameter berkorelasi (referensi luar) atau parameter fungsi tidak konstan digunakan, kami akan mengubah isi tabel Tsehingga fungsi memiliki lebih banyak pekerjaan yang harus dilakukan:

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50001), (50002), (50003), (50004), (50005);

Kueri yang dimodifikasi berikut ini sekarang menggunakan referensi luar ke tabel Tdi salah satu parameter fungsi:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Permintaan ini membutuhkan waktu sekitar 8 detik untuk mengembalikan hasil seperti:

Hasil berkorelasi

Perhatikan perbedaan waktu antara baris dalam kolom ts. The WHEREklausul membatasi hasil akhir untuk output bijaksana berukuran, tapi fungsi tidak efisien masih membutuhkan waktu untuk mengisi variabel meja dengan baris 50.000-aneh (tergantung pada nilai berkorelasi dari idari tabel T).

Rencana pelaksanaannya adalah:

Rencana eksekusi terkait

Perhatikan kurangnya operator Sequence. Sekarang, ada operator fungsi Table Valued Function tunggal yang mengisi variabel tabel dan mengembalikan barisnya pada setiap iterasi dari loop bersarang bergabung.

Agar lebih jelas: dengan hanya 5 baris pada tabel T, operator Function Table Valued berjalan 5 kali. Ini menghasilkan 50.001 baris pada iterasi pertama, 50.002 pada yang kedua ... dan seterusnya. Variabel tabel 'dibuang' (terpotong) di antara iterasi, sehingga masing-masing dari lima panggilan adalah populasi penuh. Inilah sebabnya mengapa sangat lambat, dan setiap baris membutuhkan waktu yang hampir bersamaan untuk muncul di hasilnya.

Catatan samping:

Tentu saja, skenario di atas sengaja dibuat untuk menunjukkan betapa buruknya kinerja ketika msTVF mengisi banyak baris pada setiap iterasi.

Sebuah masuk akal pelaksanaan kode di atas akan mengatur kedua parameter msTVF untuk i, dan menghapus berlebihan WHEREklausa. Variabel tabel masih akan dipotong dan diisi ulang pada setiap iterasi, tetapi hanya dengan satu baris setiap kali.

Kita juga bisa mengambil nilai minimum dan maksimum idari Tdan menyimpannya dalam variabel di langkah sebelumnya. Memanggil fungsi dengan variabel alih-alih parameter berkorelasi akan memungkinkan pola variabel tabel 'statis' digunakan seperti yang disebutkan sebelumnya.

Caching untuk parameter berkorelasi yang tidak berubah

Kembali ke menjawab pertanyaan asli sekali lagi, di mana pola statis Sequence tidak dapat digunakan, SQL Server dapat menghindari memotong dan mengisi kembali variabel tabel msTVF jika tidak ada parameter berkorelasi telah berubah sejak iterasi sebelumnya dari loop bersarang bergabung.

Untuk menunjukkan ini, kami akan mengganti konten Tdengan lima nilai yang identik i :

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50005), (50005), (50005), (50005), (50005);

Kueri dengan parameter berkorelasi lagi:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Kali ini hasilnya muncul sekitar 1,5 detik :

Hasil baris identik

Perhatikan cap waktu yang identik di setiap baris. Hasil cache dalam variabel tabel digunakan kembali untuk iterasi berikutnya di mana nilai berkorelasi itidak berubah. Menggunakan kembali hasilnya jauh lebih cepat daripada memasukkan 50.005 baris setiap kali.

Rencana eksekusi terlihat sangat mirip dengan sebelumnya:

Rencanakan untuk baris yang identik

Perbedaan utama adalah dalam properti Rebind Aktual dan Rewind Aktual dari operator Fungsi Table Valued:

Properti operator

Ketika parameter berkorelasi tidak berubah, SQL Server dapat memutar ulang (mundur) hasil saat ini dalam variabel tabel. Ketika korelasi berubah, SQL Server harus memotong dan mengisi kembali variabel tabel (rebind). Satu rebind terjadi pada iterasi pertama; keempat iterasi berikutnya semuanya mundur karena nilai T.itidak berubah.

Paul White mengatakan GoFundMonica
sumber