Chức năng định giá bảng đa giá trị so với chức năng định giá bảng nội tuyến


198

Một vài ví dụ để hiển thị, chỉ cần incase:

Bảng nội tuyến có giá trị

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

Bảng đa báo cáo có giá trị

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ó một lợi thế để sử dụng một loại (câu lệnh in-line hoặc multi) so với loại kia không? Có những kịch bản nhất định khi cái này tốt hơn cái kia hay sự khác biệt hoàn toàn là cú pháp? Tôi nhận ra hai truy vấn mẫu đang làm những việc khác nhau nhưng có lý do gì tôi sẽ viết chúng theo cách đó không?

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


Ngoài ra, một trong những lợi ích to lớn của chức năng nội tuyến là bạn có thể chọn các cột ROWID (TIMESTAMP), trong khi bạn không thể chèn dữ liệu TIMESTAMP vào bảng trả về trong chức năng đa tầng!
Artru

3
Cảm ơn cho 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 THAY ĐỔI một chức năng là ITV sang MSTV, trình hồ sơ cho rằng bạn đang thay đổi ITV. Bất kể bạn làm gì để có được cú pháp ngay từ quan điểm 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 để giải quyết vấn đề này là DROP chức năng cũ và TẠO chức năng mới dưới dạng MSTV.
Fandango68

Câu trả lời:


141

Trong 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 nói đúng, sẽ có sự khác biệt về hiệu năng giữa hàm có giá trị bảng nội tuyến (ITVF) và hàm có giá trị bảng đa câu lệnh (MSTVF) ngay cả khi cả hai chỉ thực hiện một câu lệnh CHỌN. SQL Server sẽ đối xử với ITVF giống như mộtVIEWtrong đó nó sẽ tính toán một kế hoạch thực hiện bằng cách sử dụng số liệu thống kê mới nhất trên các bảng được đề cập. MSTVF tương đương với việc nhồi toàn bộ nội dung của câu lệnh CHỌN 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ỳ số liệu 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à chúng hiếm khi như vậy), 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ừ quan điểm thống kê, nó rất đáng chú ý.

Trong trường hợp của bạn, hai chức năng không tương đương về chức năng. Hàm MSTV thực hiện một truy vấn bổ sung 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 liên kết khác vì nó sẽ cần gọi hàm cho mỗi khách hàng được thông qua. Tuy nhiên, nếu bạn đã viết lại chức năng MSTV của mình 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, trình tối ưu hóa sẽ có thể gọi hàm đó một lần và xây dựng kế hoạch thực hiện tốt hơn nhưng nó vẫn không tốt hơn một ITVS tương đương, không tham số hoặc a VIEW.

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

Cảm ơn Matt.

Thêm vào

Vì tôi thấy điều này xuất hiện gần đây, đây là một phân tích tuyệt vời được thực hiện bởi Wayne Sheffield so sánh sự khác biệt về hiệu suất giữa các chức năng Giá trị bảng nội tuyến và các chức năng Đa báo cáo.

Bài viết trên blog gốc của anh ấy.

Sao chép trên SQL Server Central


40
Điều này chỉ đơn giản là không đúng - Các hàm đa câu lệnh rất thường là một hiệu năng rất lớn bởi vì chúng ngăn trình tối ưu hóa truy vấn sử dụng số liệu thống kê. Nếu tôi có 1 đô la cho mỗi lần tôi thấy sử dụng chức năng đa câu lệnh 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 hàng trả về là 1), tôi đã có đủ để mua một chiếc xe nhỏ.
Matt Whitfield

Lời 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 liên quan: stackoverflow.com/questions/4109152/ không Đừ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

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

29

Trong nội bộ, SQL Server xử lý một hàm có giá trị của bảng nội tuyến giống như nó sẽ xem và xử lý một hàm đa bảng có giá trị tương tự như cách nó sẽ làm thủ tục được lưu trữ.

Khi một hàm có giá trị bảng nội tuyến được sử dụng như một phần của truy vấn bên ngoài, bộ xử lý truy vấn sẽ mở rộng định nghĩa UDF và tạo 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.

Đối với hàm có giá trị bảng đa câu lệnh, kế hoạch thực hiện được tạo cho chính hàm đó và được lưu trong bộ đệm của kế hoạch thực hiện (một khi chức năng đã được thực hiện lần đầu tiên). Nếu các hàm có giá trị của bảng đa câu lệnh đượ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 trả về là gì và do đó đưa ra một số giả định tiêu chuẩn - thực tế, nó giả định rằng hàm sẽ trả về một hàng duy nhất và trả về chức năng sẽ được truy cập bằng cách sử dụng quét bảng đối với một bảng có một hàng.

Trường hợp các hàm có giá trị của bảng nhiều câu lệnh có thể hoạt động kém là khi chúng trả về một số lượng lớn các hàng và được nối với các truy vấn bên ngoài. Các vấn đề về hiệu năng chủ yếu dựa vào thực tế là trình tối ưu hóa sẽ tạo ra một kế hoạch giả định rằng một hàng duy nhất được trả về, đây sẽ không nhất thiết là kế hoạch phù hợp nhất.

Như một quy tắc chung, chúng tôi đã thấy rằng các hàm có giá trị của bảng nội tuyến có thể được sử dụng ưu tiên cho các câu lệnh đa câu lệnh (khi UDF sẽ được sử dụng như một phần của truy vấn bên ngoài) do các vấn đề hiệu năng tiềm năng này.


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

6
Trừ khi bạn cần tham gia những kết quả đó trong một truy vấn khác.
Guillermo Gutiérrez

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 có giá trị bảng nhiều câu lệnh. Tốt nhất của cả hai thế giới.
Robino

13

Có một sự khác biệt khác. Một hàm có giá trị bảng 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.


3

Ví dụ của bạn, tôi nghĩ rằng, 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 dưới dạng một lựa chọn duy nhất và là lý do chính đáng để sử dụng kiểu nội tuyến. Câu lệnh thứ hai có thể được thực hiện dưới dạng một câu lệnh (sử dụng truy vấn phụ để lấy ngày tối đa), nhưng một số lập trình viên có thể thấy dễ đọc hơn hoặc tự nhiên hơn khi thực hiện nó trong nhiều câu lệnh như bạn đã làm. Một số chức năng đơn giản không thể được thực hiện trong một câu lệnh và do đó yêu cầu phiên bản đa câu lệnh.

Tôi đề nghị sử dụng cách đơ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 (rõ ràng) hoặc khi sở thích / khả năng đọc cá nhân làm cho nó trở nên gõ thêm.


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

Tôi không biết, nhưng tôi sẽ không nghĩ như vậy. Có lẽ tốt hơn là để máy chủ sql tìm ra các tối ưu hóa mà bạn có thể cố gắng thực hiện thủ công (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ể thực hiện một số thử nghiệm hiệu suất để chứng minh / từ chối điều này trong các trường hợp cụ thể.
Ray

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


0

Tôi đã không kiểm tra điều này, nhưng một hàm đa câu lệnh lưu trữ tập kết quả. Có thể có trường hợp có quá nhiều thứ đang diễn ra để trình tối ưu hóa thực hiện chức năng. Ví dụ: giả sử bạn có một hàm trả về kết quả từ các cơ sở dữ liệu khác nhau tùy thuộc vào nội dung bạn chuyển dưới dạng "Số công ty". Thông thường, bạn có thể tạo chế độ xem với một liên minh sau đó lọc theo số công ty nhưng tôi thấy rằng đôi khi máy chủ sql kéo lại toàn bộ liên minh và không đủ thông minh để gọi một liên kết. Một hàm bảng có thể có logic để chọn nguồn.


0

Một trường hợp khác để sử dụng hàm đa dòng sẽ là phá vỡ máy chủ sql khỏi đẩy mệnh đề where.

Ví dụ: tôi có một bảng có tên bảng và một số tên bảng được định dạng như C05_2019 và C12_2018 và tất cả các bảng được định dạng theo cách đó có cùng một lược đồ. Tôi muốn hợp nhất tất cả dữ liệu đó vào một bảng và phân tích 05 và 12 thành một cột CompNo và 2018,2019 thành một cột năm. Tuy nhiên, có các bảng khác như ACA_StoolTable mà tôi không thể trích xuất CompNo và CompYr và sẽ gặp lỗi chuyển đổi nếu tôi thử. Vì vậy, truy vấn của tôi gồm hai phần, một truy vấn bên trong chỉ trả về các bảng được định dạng như 'C_____' thì truy vấn bên ngoài đã thực hiện chuyển đổi chuỗi con và int. tức là Cast (Chuỗi con (2, 2) là int) dưới dạng CompNo. Tất cả đều có vẻ tốt ngoại trừ máy chủ sql đã quyết định đặt chức năng Cast của tôi trước khi kết quả được lọc và do đó tôi nhận được lỗi chuyển đổi tâm trí. Một hàm đa bảng có thể ngăn điều đó xảy ra,


0

Có lẽ trong một cách rất cô đọng. ITVF (TVF nội tuyến): hơn nữa nếu bạn là người DB, là loại chế độ xem được tham số hóa, hãy chọn một CHỌN duy nhất

MTVF (Multi-statement TVF): Nhà phát triển, tạo và tải một biến bảng.


-2

nếu bạn định thực hiện một truy vấn, bạn có thể tham gia vào chức năng Giá trị bảng nội tuyến như sau:

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

nó sẽ phát sinh ít chi phí và chạy tốt

nếu bạn cố gắng sử dụng Bảng đa báo cáo có giá trị trong một truy vấn tương tự, bạn sẽ gặp vấn đề về hiệu năng:

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 chức năng 1 lần cho mỗi hàng được trả về, vì tập kết quả sẽ lớn hơn, nó sẽ chạy chậm hơn và chậm hơn.


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

1
Không, cả hai đều trả về một bảng, điều này 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 vào một cột.
cjk

1
@ck, tôi đã cập nhật truy vấn yo 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 làm truy vấn phụ, điều này sẽ dẫn đến hiệu suất kém hơn.
KM.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.