Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Hiệu suất của bên ngoài áp dụng với chức năng

Nó phụ thuộc vào loại chức năng:

  1. Nếu hàm là một hàm có giá trị bảng nội tuyến thì hàm này sẽ được coi là một dạng xem "được tham số hóa" và SQL Server có thể thực hiện một số công việc tối ưu hóa.

  2. Nếu hàm là hàm có giá trị bảng nhiều bước thì sẽ khó đối với SQL Server để tối ưu hóa câu lệnh và kết quả từ SET STATISTICS IO sẽ gây hiểu lầm.

Đối với thử nghiệm tiếp theo, tôi đã sử dụng AdventureWorks2008 (bạn có thể tải xuống cơ sở dữ liệu này từ CodePlex). Trong cơ sở dữ liệu mẫu này, bạn có thể tìm thấy một inline table-valued function được đặt tên là [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

Tôi đã tạo một chức năng mới có tên [Sales].[ufnGetCheapestProductMultiStep] . Hàm này là một 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

Bây giờ, chúng tôi có thể chạy các thử nghiệm tiếp theo:

--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

Và đây là kết quả từ SQL Profiler :

Kết luận :bạn có thể thấy rằng bằng cách sử dụng truy vấn hoặc hàm có giá trị bảng nội tuyến với OUTER APPLY sẽ cung cấp cho bạn cùng một hiệu suất (đọc hợp lý). Thêm vào đó: các hàm có giá trị bảng nhiều bước (thường) đắt hơn .

Lưu ý :Tôi không khuyên bạn nên sử dụng SET STATISTICS IO để đo lường IO cho các hàm có giá trị bảng vô hướng và nhiều bước vì kết quả có thể sai. Ví dụ:đối với các bài kiểm tra này, kết quả đầu ra từ SET STATISTICS IO ON sẽ là:

--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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Lấy ngày bắt đầu tuần và ngày kết thúc tuần từ số tuần

  2. Cách nối chuỗi trong SQL

  3. Trả lại hạt giống ban đầu của cột danh tính trong SQL Server

  4. Sử dụng DB_ID () để trả về ID của cơ sở dữ liệu trong SQL Server

  5. công thức cho cột được tính dựa trên cột của bảng khác nhau