Apakah ada manfaat untuk MEMASANG fungsi di luar Perlindungan Halloween?

52

Sudah diketahui umum bahwa SCHEMABINDINGsuatu fungsi dapat menghindari spool yang tidak perlu dalam rencana pembaruan:

Jika Anda menggunakan UDF T-SQL sederhana yang tidak menyentuh tabel apa pun (mis. Jangan mengakses data), pastikan Anda menentukan SCHEMABINDINGopsi selama pembuatan UDF. Ini akan membuat skema UDF terikat dan memastikan bahwa optimizer kueri tidak menghasilkan operator spool yang tidak perlu untuk rencana kueri yang melibatkan UDF ini.

Apakah ada keuntungan lain dari SCHEMABINDINGsuatu fungsi, bahkan jika itu tidak mengakses data?

Paul White
sumber

Jawaban:

78

Iya.

Gagal menentukan WITH SCHEMABINDINGberarti SQL Server melewatkan pemeriksaan terperinci yang biasanya dilakukan pada fungsi tubuh. Ini hanya menandai fungsi sebagai mengakses data (seperti yang disebutkan dalam tautan yang diberikan dalam pertanyaan).

Ini adalah pengoptimalan kinerja. Jika tidak membuat asumsi ini, SQL Server harus melakukan pemeriksaan terperinci pada setiap pemanggilan fungsi (karena fungsi tidak terikat dapat berubah kapan saja).

Ada lima properti fungsi penting:

  • Determinisme
  • Presisi
  • Akses data
  • Akses Data Sistem
  • Verifikasi Sistem

Misalnya, ambil fungsi skalar tidak terikat berikut:

CREATE FUNCTION dbo.F
(
    @i integer
)
RETURNS datetime
AS
BEGIN
    RETURN '19000101';
END;

Kita bisa melihat lima properti menggunakan fungsi metadata:

SELECT 
    IsDeterministic = OBJECTPROPERTYEX(Func.ID, 'IsDeterministic'),
    IsPrecise = OBJECTPROPERTYEX(Func.ID, 'IsPrecise'),
    IsSystemVerified = OBJECTPROPERTYEX(Func.ID, 'IsSystemVerified'),
    UserDataAccess = OBJECTPROPERTYEX(Func.ID, 'UserDataAccess'),
    SystemDataAccess = OBJECTPROPERTYEX(Func.ID, 'SystemDataAccess')
FROM (VALUES(OBJECT_ID(N'dbo.F', N'FN'))) AS Func (ID);

Hasil

Dua properti akses data telah diset true, dan tiga lainnya diset false .

Ini memiliki implikasi di luar yang mungkin diharapkan (misalnya, digunakan dalam tampilan yang diindeks atau kolom yang dihitung dengan indeks).

Efek pada pengoptimal kueri

The Determinisme properti khususnya mempengaruhi query optimizer. Ini memiliki aturan rinci tentang jenis penulisan ulang dan manipulasi yang diizinkan untuk dilakukan, dan ini sangat terbatas untuk elemen non-deterministik. Efek sampingnya bisa sangat halus.

Misalnya, perhatikan dua tabel berikut:

CREATE TABLE dbo.T1
(
    SomeInteger integer PRIMARY KEY
);
GO
CREATE TABLE dbo.T2
(
    SomeDate datetime PRIMARY KEY
);

... dan kueri yang menggunakan fungsi (seperti yang didefinisikan sebelumnya):

SELECT * 
FROM dbo.T1 AS T1
JOIN dbo.T2 AS T2
    ON T2.SomeDate = dbo.F(T1.SomeInteger);

Rencana kueri seperti yang diharapkan, menampilkan tabel seek T2:

Carilah rencana

Namun, jika kueri logis yang sama ditulis menggunakan tabel turunan atau ekspresi tabel umum:

WITH CTE AS
(
    SELECT *, dt = dbo.F(T1.SomeInteger) 
    FROM dbo.T1 AS T1
)
SELECT * 
FROM CTE
JOIN dbo.T2 AS T2
    ON T2.SomeDate = CTE.dt;

-- Derived table
SELECT
    *
FROM 
(
    SELECT *, dt = dbo.F(T1.SomeInteger)
    FROM dbo.T1 AS T1
) AS T1
JOIN dbo.T2 AS T2
    ON T2.SomeDate = T1.dt;

Paket eksekusi sekarang menampilkan pemindaian, dengan predikat yang melibatkan fungsi tersangkut dalam Filter:

Rencana pemindaian

Ini juga terjadi jika tabel yang diturunkan atau ekspresi tabel umum digantikan oleh fungsi tampilan atau in-line. Sebuah FORCESEEKpetunjuk (dan upaya lain yang serupa) tidak akan berhasil:

Pesan eror

Masalah mendasar adalah bahwa pengoptimal kueri tidak dapat menyusun ulang elemen permintaan nondeterministic secara bebas .

Untuk menghasilkan pencarian, predikat Filter perlu dipindahkan ke paket akses T2. Gerakan ini dicegah ketika fungsinya non-deterministik.

Memperbaiki

Perbaikan untuk contoh ini melibatkan dua langkah:

  1. Menambahkan WITH SCHEMABINDING
  2. Buat fungsi deterministik

Langkah pertama adalah sepele. Yang kedua melibatkan menghilangkan cast implisit non-deterministik dari string ke datetime; menggantinya dengan deterministik CONVERT. Tidak cukup dengan sendirinya .

ALTER FUNCTION dbo.F
(
    @i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
    -- Convert with a deterministic style
    RETURN CONVERT(datetime, '19000101', 112);
END;

Properti fungsi sekarang:

Properti baru

Dengan pengoptimal yang dibebaskan, semua contoh sekarang menghasilkan rencana pencarian yang diinginkan .


Perhatikan bahwa menggunakan CASTto datetimedalam fungsi tidak akan berfungsi, karena tidak mungkin menentukan gaya konversi di sintaks itu:

ALTER FUNCTION dbo.F
(
    @i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
    -- Convert with a deterministic style
    RETURN CAST('19000101' AS datetime);
END;

Definisi fungsi ini menghasilkan rencana pemindaian, dan properti menunjukkannya tetap non-deterministik:

Properti fungsi CAST

Paul White
sumber