Itu tergantung dari jenis fungsi:
-
Jika fungsinya adalah fungsi bernilai tabel sebaris, maka fungsi ini akan dianggap sebagai tampilan "berparameter" dan
SQL Server
dapat melakukan beberapa pekerjaan pengoptimalan. -
Jika fungsinya adalah fungsi bernilai tabel multi-langkah maka sulit untuk
SQL Server
untuk mengoptimalkan pernyataan dan output dariSET STATISTICS IO
akan menyesatkan.
Untuk pengujian selanjutnya saya menggunakan AdventureWorks2008
(Anda dapat mengunduh database ini dari CodePlex). Dalam database contoh ini, Anda mungkin menemukan inline table-valued function
bernama [Sales].[ufnGetCheapestProduct]
:
ALTER FUNCTION [Sales].[ufnGetCheapestProduct](@ProductID INT)
RETURNS TABLE
AS
RETURN
SELECT dt.ProductID
,dt.UnitPrice
FROM
(
SELECT d.SalesOrderDetailID
,d.UnitPrice
,d.ProductID
,ROW_NUMBER() OVER(PARTITION BY d.ProductID ORDER BY d.UnitPrice ASC, d.SalesOrderDetailID) RowNumber
FROM Sales.SalesOrderDetail d
WHERE d.ProductID = @ProductID
) dt
WHERE dt.RowNumber = 1
Saya membuat fungsi baru bernama [Sales].[ufnGetCheapestProductMultiStep]
. Fungsi ini adalah multi-step table-valued function
:
CREATE FUNCTION [Sales].[ufnGetCheapestProductMultiStep](@ProductID INT)
RETURNS @Results TABLE (ProductID INT PRIMARY KEY, UnitPrice MONEY NOT NULL)
AS
BEGIN
INSERT @Results(ProductID, UnitPrice)
SELECT dt.ProductID
,dt.UnitPrice
FROM
(
SELECT d.SalesOrderDetailID
,d.UnitPrice
,d.ProductID
,ROW_NUMBER() OVER(PARTITION BY d.ProductID ORDER BY d.UnitPrice ASC, d.SalesOrderDetailID) RowNumber
FROM Sales.SalesOrderDetail d
WHERE d.ProductID = @ProductID
) dt
WHERE dt.RowNumber = 1;
RETURN;
END
Sekarang, kita dapat menjalankan tes berikutnya:
--Test 1
SELECT p.ProductID, p.Name, oa1.*
FROM Production.Product p
OUTER APPLY
(
SELECT dt.ProductID
,dt.UnitPrice
FROM
(
SELECT d.SalesOrderDetailID
,d.UnitPrice
,d.ProductID
,ROW_NUMBER() OVER(PARTITION BY d.ProductID ORDER BY d.UnitPrice ASC, d.SalesOrderDetailID) RowNumber
FROM Sales.SalesOrderDetail d
WHERE d.ProductID = p.ProductID
) dt
WHERE dt.RowNumber = 1
) oa1
--Test 2
SELECT p.ProductID, p.Name, oa2.*
FROM Production.Product p
OUTER APPLY [Sales].[ufnGetCheapestProduct](p.ProductID) oa2
--Test 3
SELECT p.ProductID, p.Name, oa3.*
FROM Production.Product p
OUTER APPLY [Sales].[ufnGetCheapestProductMultiStep](p.ProductID) oa3
Dan ini adalah output dari SQL Profiler
:
Kesimpulan :Anda dapat melihatnya menggunakan kueri atau fungsi bernilai tabel sebaris dengan OUTER APPLY
akan memberi Anda kinerja yang sama (pembacaan logis). Plus:fungsi bernilai tabel multi-langkah (biasanya) lebih mahal
.
Catatan :Saya tidak menyarankan menggunakan SET STATISTICS IO
untuk mengukur IO
untuk skalar dan tabel multi-langkah bernilai fungsi karena hasilnya bisa salah. Misalnya, untuk pengujian ini output dari SET STATISTICS IO ON
akan menjadi:
--Test 1
Table 'SalesOrderDetail'. Scan count 504, logical reads 1513, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Test 2
Table 'SalesOrderDetail'. Scan count 504, logical reads 1513, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Test 3
Table '#064EAD61'. Scan count 504, logical reads 1008 /*WRONG*/, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.