To nie jest gwarantowane.
Musisz sprawdzić plan wykonania, aby się dowiedzieć. Kilka przykładów.
CREATE FUNCTION dbo.FUNC1(@p1 int)
RETURNS int
AS
BEGIN
RETURN @p1 + 1
END
GO
CREATE FUNCTION dbo.FUNC2(@p1 int)
RETURNS int
WITH SCHEMABINDING
AS
BEGIN
RETURN @p1 + 1
END
GO
SELECT
OBJECTPROPERTYEX(OBJECT_ID('dbo.FUNC1'), 'IsDeterministic'),
OBJECTPROPERTYEX(OBJECT_ID('dbo.FUNC2'), 'IsDeterministic')
GO
FUNC2
powstaje WITH SCHEMABINDING
i jest traktowany jako deterministyczny. FUNC1
nie jest.
SELECT
dbo.FUNC1(number) AS FUNC1,
dbo.FUNC2(number) AS FUNC2
FROM master..spt_values
WHERE dbo.FUNC1(number) >= 5 AND dbo.FUNC2(number) >= 5
ORDER BY dbo.FUNC1(number), dbo.FUNC2(number)
Daje plan
|--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC))
|--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])))
|--Filter(WHERE:([test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])>=(5) AND [Expr1004]>=(5)))
|--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])))
|--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
FUNC1
oceniana jest dwukrotnie (po w filtrze, a raz w skalara obliczeniowej wyprowadzania obliczoną kolumna zarówno występ oraz zamawiania) FUNC2
jest oceniany tylko raz.
Przepisywanie jako
SELECT
FUNC1,
FUNC2
FROM master..spt_values
CROSS APPLY (SELECT dbo.FUNC1(number), dbo.FUNC2(number)) C(FUNC1, FUNC2)
WHERE FUNC1 >= 5 AND FUNC2 >= 5
ORDER BY FUNC1, FUNC2
zmianę planu lekko i oba są oceniane tylko raz
|--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC))
|--Filter(WHERE:([Expr1003]>=(5)))
|--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])))
|--Filter(WHERE:([Expr1004]>=(5)))
|--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])))
|--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
teraz co nieznaczne zmiany do zapytania
SELECT
FUNC1 + 10,
FUNC2 + 10
FROM master..spt_values
CROSS APPLY (SELECT dbo.FUNC1(number), dbo.FUNC2(number)) C(FUNC1, FUNC2)
WHERE FUNC1 >= 5 AND FUNC2 >= 5
ORDER BY FUNC1, FUNC2
Podaje odwrotność pierwotnego wyniku, ponieważ FUNC2
jest dwukrotnie oceniany, ale FUNC1
tylko raz.
|--Compute Scalar(DEFINE:([Expr1005]=[Expr1003]+(10)))
|--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC))
|--Filter(WHERE:([Expr1003]>=(5)))
|--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])))
|--Filter(WHERE:([Expr1004]>=(5)))
|--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number]), [Expr1006]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])+(10)))
|--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
Skąd wiesz, że został on wywołany dwa razy? –
Plan pokazuje, że jest on wywoływany raz w wyrażeniu filtru, następnie kolejnym operatorem jest skalar obliczeniowy, który ponownie wywołuje funkcję, wyświetla wynik jako kolumnę "Expr1003", a kolumna jest używana zarówno dla wyboru, jak i dla porządku przez . –
+ dobry przykład. Założę się, że UDF bez tabel jest zawsze * oceniany raz na wiersz dla każdej wzmianki, chyba że istnieje oczywisty skrót (np. Wyrażenie ORDER BY dokładnie pasuje do wyrażenia SELECT) – gbn