Sì.
Se non si specifica WITH SCHEMABINDING
, SQL Server ignora i controlli dettagliati che esegue normalmente sul corpo della funzione. Contrassegna semplicemente la funzione come accesso ai dati (come indicato nel collegamento indicato nella domanda).
Questa è un'ottimizzazione delle prestazioni. In caso contrario, SQL Server dovrebbe eseguire i controlli dettagliati su ogni chiamata di funzione (poiché la funzione non associata potrebbe cambiare in qualsiasi momento).
Esistono cinque importanti proprietà delle funzioni:
- Determinismo
- Precisione
- Accesso ai dati
- Accesso ai dati di sistema
- Verifica del sistema
Ad esempio, prendi la seguente funzione scalare non associata:
CREATE FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
AS
BEGIN
RETURN '19000101';
END;
Possiamo esaminare le cinque proprietà usando una funzione di metadati:
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);
Le due proprietà di accesso ai dati sono state impostate su true e le altre tre sono impostate su false .
Ciò ha implicazioni oltre a quelle che ci si potrebbe aspettare (ad esempio, utilizzare in viste indicizzate o colonne calcolate indicizzate).
Effetti su Query Optimizer
La proprietà Determinism in particolare influisce su Query Optimizer. Ha regole dettagliate riguardanti i tipi di riscritture e manipolazioni che può eseguire, e queste sono molto limitate per gli elementi non deterministici. Gli effetti collaterali possono essere abbastanza sottili.
Ad esempio, considerare le due tabelle seguenti:
CREATE TABLE dbo.T1
(
SomeInteger integer PRIMARY KEY
);
GO
CREATE TABLE dbo.T2
(
SomeDate datetime PRIMARY KEY
);
... e una query che utilizza la funzione (come definita in precedenza):
SELECT *
FROM dbo.T1 AS T1
JOIN dbo.T2 AS T2
ON T2.SomeDate = dbo.F(T1.SomeInteger);
Il piano di query è come previsto, con una ricerca nella tabella T2:
Tuttavia, se la stessa query logica viene scritta utilizzando una tabella derivata o un'espressione di tabella comune:
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;
Il piano di esecuzione ora presenta una scansione, con il predicato che coinvolge la funzione bloccata in un filtro:
Ciò accade anche se la tabella derivata o l'espressione di tabella comune viene sostituita da una vista o una funzione in linea. Un FORCESEEK
suggerimento (e altri tentativi simili) non avrà esito positivo:
Il problema fondamentale è che Query Optimizer non può riordinare gli elementi di query non deterministici come liberamente .
Per produrre una ricerca, il predicato Filter dovrebbe essere spostato nel piano per accedere ai dati T2. Questo movimento è impedito quando la funzione non è deterministica.
fix
La correzione per questo esempio prevede due passaggi:
- Inserisci
WITH SCHEMABINDING
- Rendi deterministica la funzione
Il primo passo è banale. Il secondo implica la rimozione del cast implicito non deterministico dalla stringa a datetime
; sostituendolo con un deterministico CONVERT
. Nessuno dei due è sufficiente da solo .
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CONVERT(datetime, '19000101', 112);
END;
Le proprietà della funzione sono ora:
Con l'ottimizzatore liberato, tutti gli esempi ora producono il piano di ricerca desiderato .
Si noti che l'utilizzo di un CAST
to datetime
nella funzione non funzionerebbe, poiché non è possibile specificare uno stile di conversione in quella sintassi:
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CAST('19000101' AS datetime);
END;
Questa definizione di funzione produce il piano di scansione e le proprietà mostrano che rimane non deterministico: