2010-03-31 29 views
166

Một vài ví dụ để cho thấy, chỉ trong trường hợp:Multi-tuyên bố Bảng Quý Chức năng vs Inline Bảng Function Quý

Inline Bảng Quý

CREATE FUNCTION MyNS.GetUnshippedOrders() 
RETURNS TABLE 
AS 
RETURN SELECT a.SaleId, a.CustomerID, b.Qty 
    FROM Sales.Sales a INNER JOIN Sales.SaleDetail b 
     ON a.SaleId = b.SaleId 
     INNER JOIN Production.Product c ON b.ProductID = c.ProductID 
    WHERE a.ShipDate IS NULL 
GO 

đa Statement Bảng Quý

CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT) 
RETURNS @CustomerOrder TABLE 
(SaleOrderID INT   NOT NULL, 
CustomerID  INT   NOT NULL, 
OrderDate  DATETIME NOT NULL, 
OrderQty  INT   NOT NULL) 
AS 
BEGIN 
    DECLARE @MaxDate DATETIME 

    SELECT @MaxDate = MAX(OrderDate) 
    FROM Sales.SalesOrderHeader 
    WHERE CustomerID = @CustomerID 

    INSERT @CustomerOrder 
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty 
    FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b 
     ON a.SalesOrderID = b.SalesOrderID 
     INNER JOIN Production.Product c ON b.ProductID = c.ProductID 
    WHERE a.OrderDate = @MaxDate 
     AND a.CustomerID = @CustomerID 
    RETURN 
END 
GO 

Có lợi thế nào khi sử dụng một loại (tuyên bố nội dòng hoặc đa) trên othe r? Có những tình huống nào đó khi một người tốt hơn người kia hay là sự khác biệt hoàn toàn là cú pháp? Tôi nhận ra hai ví dụ truy vấn đang làm những việc khác nhau nhưng có một lý do tôi sẽ viết chúng theo cách đó?

Đọc về chúng và những lợi thế/khác biệt chưa thực sự được giải thích.

+0

Ngoài ra một trong những lợi ích rất lớn của hàm inline là bạn có thể chọn ROWID cột (TIMESTAMP), trong khi bạn không thể chèn dữ liệu dấu thời gian vào bảng trở lại trong chức năng multistatement! – Artru

+3

Cảm ơn một chủ đề tuyệt vời. Tôi đã học được rất nhiều. Tuy nhiên, một điều cần lưu ý là khi ALTERing một chức năng ITV đến MSTV, profiler cho rằng bạn đang thay đổi một ITV. Không có vấn đề gì bạn làm để có được cú pháp ngay từ quan điểm của MSTV, việc biên dịch lại luôn thất bại, thường là xung quanh câu lệnh đầu tiên sau BEGIN. Cách duy nhất xung quanh điều này là để DROP chức năng cũ và TẠO cái mới như một MSTV. – Fandango68

Trả lời

121

Khi nghiên cứu nhận xét của Matt, tôi đã sửa đổi tuyên bố ban đầu của mình. Ông là chính xác, sẽ có một sự khác biệt về hiệu suất giữa một hàm bảng có giá trị nội tuyến (ITVF) và một bảng đa tuyên bố có giá trị hàm (MSTVF) ngay cả khi chúng vừa thực thi câu lệnh SELECT. SQL Server sẽ xử lý một ITVF phần nào giống như một VIEW ở chỗ nó sẽ tính toán một kế hoạch thực hiện bằng cách sử dụng các số liệu thống kê mới nhất về các bảng được đề cập. Một MSTVF tương đương với nhồi toàn bộ nội dung của câu lệnh SELECT của bạn vào một biến bảng và sau đó tham gia vào đó. Do đó, trình biên dịch không thể sử dụng bất kỳ thống kê bảng nào trên các bảng trong MSTVF. Vì vậy, tất cả mọi thứ đều bình đẳng, (mà họ hiếm khi), ITVF sẽ hoạt động tốt hơn MSTVF. Trong các thử nghiệm của tôi, sự khác biệt hiệu suất trong thời gian hoàn thành là không đáng kể tuy nhiên từ một quan điểm thống kê, nó đã được chú ý.

Trong trường hợp của bạn, hai hàm không có chức năng tương đương. Hàm MSTV thực hiện một truy vấn phụ mỗi lần nó được gọi và, quan trọng nhất là các bộ lọc trên id khách hàng. Trong một truy vấn lớn, trình tối ưu hóa sẽ không thể tận dụng các loại kết nối khác vì nó sẽ cần gọi hàm cho mỗi customerId được thông qua.Tuy nhiên, nếu bạn đã viết lại chức năng MSTV bạn như vậy:

CREATE FUNCTION MyNS.GetLastShipped() 
RETURNS @CustomerOrder TABLE 
    (
    SaleOrderID INT   NOT NULL, 
    CustomerID  INT   NOT NULL, 
    OrderDate  DATETIME NOT NULL, 
    OrderQty  INT   NOT NULL 
    ) 
AS 
BEGIN 
    INSERT @CustomerOrder 
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty 
    FROM Sales.SalesOrderHeader a 
     INNER JOIN Sales.SalesOrderHeader b 
      ON a.SalesOrderID = b.SalesOrderID 
     INNER JOIN Production.Product c 
      ON b.ProductID = c.ProductID 
    WHERE a.OrderDate = (
         Select Max(SH1.OrderDate) 
         FROM Sales.SalesOrderHeader As SH1 
         WHERE SH1.CustomerID = A.CustomerId 
         ) 
    RETURN 
END 
GO 

Trong một truy vấn, tôi ưu hoa sẽ có thể gọi là chức năng một lần và xây dựng một kế hoạch thực hiện tốt hơn nhưng nó vẫn sẽ không tốt hơn so với một tương đương , ITVS không tham số hoặc VIEW.

ITVFs nên được ưu tiên hơn MSTVF khi khả thi vì các kiểu dữ liệu, nullability và collation từ các cột trong bảng trong khi bạn khai báo các thuộc tính đó trong bảng multi-statement có giá trị hàm và quan trọng là bạn sẽ nhận được kế hoạch thực hiện tốt hơn ITVF. Theo kinh nghiệm của tôi, tôi đã không tìm thấy nhiều trường hợp mà ITVF là một lựa chọn tốt hơn so với VIEW nhưng số dặm có thể thay đổi.

Nhờ Matt.

Addition

Vì tôi thấy điều này đưa ra thời gian gần đây, đây là một phân tích xuất sắc thực hiện bởi Wayne Sheffield so sánh sự khác biệt về hiệu năng giữa Inline Bảng chức năng có giá trị và chức năng Multi-Tuyên Bố.

His original blog post.

Copy on SQL Server Central

+37

Điều này đơn giản là không đúng - Chức năng Multi-statement rất thường xuyên là một hit hiệu suất rất lớn bởi vì họ ngừng tối ưu hóa truy vấn từ việc sử dụng số liệu thống kê. Nếu tôi có $ 1 cho mỗi lần tôi thấy việc sử dụng hàm đa tuyên bố gây ra sự lựa chọn rất kém về kế hoạch thực hiện (chủ yếu là vì nó thường ước tính số hàng trả về là 1), tôi có đủ để mua một chiếc xe nhỏ. –

+0

Giải thích tốt nhất mà tôi từng tìm thấy là trong câu trả lời đầu tiên và bài đăng có liên quan: http://stackoverflow.com/questions/4109152/table-valued-function-killing-my-query-performance Đừng bỏ lỡ tài liệu liên quan, bạn có thể đọc nó một cách nhanh chóng, và nó cực kỳ thú vị. – JotaBe

+0

Sẽ có bản cập nhật cho câu trả lời này cho SQL Server 2017 không ?: https://www.youtube.com/watch?time_continue=2&v=szTmo6rTUjM – Ralph

3

Ví dụ của bạn, tôi nghĩ, trả lời câu hỏi rất tốt. Chức năng đầu tiên có thể được thực hiện như một lựa chọn duy nhất, và là một lý do tốt để sử dụng kiểu nội tuyến. Điều thứ hai có thể được thực hiện như một câu lệnh đơn (sử dụng truy vấn con để có ngày tối đa), nhưng một số người lập trình có thể thấy dễ đọc hoặc tự nhiên hơn để làm điều đó trong nhiều câu lệnh như bạn đã làm. Một số hàm chỉ đơn giản là không thể thực hiện trong một câu lệnh và do đó yêu cầu phiên bản multi-statement.

Tôi khuyên bạn nên sử dụng đơn giản nhất (nội tuyến) bất cứ khi nào có thể và sử dụng nhiều câu lệnh khi cần thiết (hiển nhiên) hoặc khi tùy chọn/khả năng đọc cá nhân làm cho việc nhập liệu trở nên thú vị hơn.

+0

Cảm ơn câu trả lời. Vì vậy, về cơ bản, đa tuyên bố chỉ thực sự được sử dụng khi hàm phức tạp hơn là khả thi trong một hàm nội tuyến, vì lợi ích của khả năng đọc? Có bất kỳ lợi ích hiệu suất nào đối với đa tuyên bố không? – AndrewC

+0

Tôi không biết, nhưng tôi sẽ không nghĩ vậy. Nó có lẽ là tốt hơn để cho máy chủ sql tìm ra tối ưu hóa mà bạn có thể cố gắng thực hiện bằng tay (bằng cách sử dụng các biến, bảng tạm thời, hoặc bất cứ điều gì). Mặc dù bạn chắc chắn có thể làm một số thử nghiệm hiệu suất để chứng minh/bác bỏ điều này trong các trường hợp cụ thể. – Ray

+0

Rất cám ơn một lần nữa. Tôi có thể nhìn sâu hơn vào điều này khi tôi có nhiều thời gian hơn! :) – AndrewC

-2

nếu bạn đang đi để làm một truy vấn mà bạn có thể tham gia vào chức năng Quý Inline Bảng của bạn như:

SELECT 
    a.*,b.* 
    FROM AAAA a 
     INNER JOIN MyNS.GetUnshippedOrders() b ON a.z=b.z 

nó sẽ phải chịu ít overhead và chạy tốt.

nếu bạn cố gắng sử dụng của bạn Tuyên bố Bảng đa Quý trong một truy vấn tương tự, bạn sẽ có vấn đề hiệu suất:

SELECT 
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty 
    FROM xxxx x 

bởi vì bạn sẽ thực hiện thời gian chức năng 1 cho mỗi hàng trở lại, như các tập kết quả được lớn, nó sẽ chạy chậm hơn và chậm hơn.

+0

Ah, vì vậy bạn sẽ nói rằng nội tuyến là tốt hơn nhiều về hiệu suất? – AndrewC

+1

Không, cả hai đều trả về một bảng, làm cho SQL thứ hai của bạn không hợp lệ khi bạn đang cố gắng đặt một bảng trong một cột. – cjk

+1

@ck, tôi đã cập nhật truy vấn mà bạn đã nhận xét. các tham số của hàm được sử dụng trong hàm thứ hai cho vay nó được sử dụng như một truy vấn phụ, điều này sẽ dẫn đến hiệu suất kém hơn. –

24

Bên trong, SQL Server đối xử với một chức năng bảng nội tuyến có giá trị giống như nó sẽ một cái nhìn và đối xử với một hàm bảng đa tuyên bố có giá trị tương tự như cách nó sẽ là một thủ tục lưu trữ. Khi một hàm nội suy có giá trị được sử dụng như một phần của truy vấn bên ngoài, bộ xử lý truy vấn mở rộng định nghĩa UDF và tạo ra một kế hoạch thực hiện truy cập các đối tượng bên dưới, sử dụng các chỉ mục trên các đối tượng này. Quay lại đầu trang

Đối với bảng đa chức năng có giá trị, kế hoạch thực hiện được tạo cho chính hàm đó và được lưu trữ trong bộ đệm kế hoạch thực hiện (khi chức năng đã được thực hiện lần đầu tiên). Nếu các hàm đa giá trị được sử dụng như một phần của các truy vấn lớn hơn thì trình tối ưu hóa không biết hàm nào trả về, và do đó làm cho một số giả định tiêu chuẩn - có nghĩa là hàm sẽ trả về một hàng và trả về chức năng này sẽ được truy cập bằng cách sử dụng một bảng quét đối với một bảng với một hàng duy nhất.

Trong trường hợp bảng đa tuyên bố có giá trị chức năng có thể hoạt động kém là khi chúng trả lại một số lượng lớn hàng và được nối với các truy vấn bên ngoài. Các vấn đề hiệu suất chủ yếu là do thực tế là người tối ưu hóa sẽ tạo ra một kế hoạch giả định rằng một hàng đơn được trả về, mà không nhất thiết phải là kế hoạch thích hợp nhất. Theo nguyên tắc chung, chúng tôi nhận thấy rằng nếu có thể sử dụng các hàm có giá trị trong bảng nội tuyến, tùy thuộc vào các câu lệnh multi-statement (khi UDF được sử dụng như một phần của truy vấn bên ngoài). .

+2

Mặc dù có thể xử lý các hàm đa trị có giá trị tương tự như quy trình được lưu trữ, thủ tục lưu trữ giống hệt nhau chức năng nhanh hơn rất nhiều so với chức năng bảng có giá trị cho các tập dữ liệu lớn. Tôi đang gắn bó với procs được lưu trữ trên bảng đa chức năng có giá trị. – Kekoa

+5

Trừ khi bạn cần tham gia các kết quả đó trong truy vấn khác. –

+0

tại sao không sử dụng cả hai? Một proc được lưu trữ trả về kết quả của hàm multi-statement table-valued. Tốt nhất của cả hai thế giới. – Robino

11

Có một điểm khác biệt. Một hàm bảng có giá trị nội tuyến có thể được chèn vào, cập nhật và xóa khỏi - giống như một khung nhìn. Áp dụng các hạn chế tương tự - không thể cập nhật các chức năng bằng cách sử dụng tổng hợp, không thể cập nhật các cột được tính toán, v.v.

Các vấn đề liên quan