Hiệu suất TVF đa tuyên bố và hiệu suất TVF nội tuyến


18

So sánh một số câu trả lời cho câu hỏi Palindrome (chỉ 10k + người dùng, vì tôi đã xóa câu trả lời), tôi nhận được kết quả khó hiểu.

Tôi đã đề xuất một TVF đa lược đồ, ràng buộc lược đồ mà tôi nghĩ sẽ nhanh hơn so với việc chạy một hàm tiêu chuẩn. Tôi cũng có ấn tượng rằng TVF đa tuyên bố sẽ được "nội tuyến", mặc dù tôi đã sai về số đó, như bạn sẽ thấy bên dưới. Câu hỏi này là về sự khác biệt hiệu suất của hai phong cách TVF đó. Đầu tiên, bạn sẽ cần xem mã.

Đây là TVF đa tuyên bố:

IF OBJECT_ID('dbo.IsPalindrome') IS NOT NULL
DROP FUNCTION dbo.IsPalindrome;
GO
CREATE FUNCTION dbo.IsPalindrome
(
    @Word NVARCHAR(500)
) 
RETURNS @t TABLE
(
    IsPalindrome BIT NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @IsPalindrome BIT;
    DECLARE @LeftChunk NVARCHAR(250);
    DECLARE @RightChunk NVARCHAR(250);
    DECLARE @StrLen INT;
    DECLARE @Pos INT;
    SET @RightChunk = '';
    SET @IsPalindrome = 0;
    SET @StrLen = LEN(@Word) / 2;
    IF @StrLen % 2 = 1 SET @StrLen = @StrLen - 1;
    SET @Pos = LEN(@Word);
    SET @LeftChunk = LEFT(@Word, @StrLen);
    WHILE @Pos > (LEN(@Word) - @StrLen)
    BEGIN
        SET @RightChunk = @RightChunk + SUBSTRING(@Word, @Pos, 1)
        SET @Pos = @Pos - 1;
    END
    IF @LeftChunk = @RightChunk SET @IsPalindrome = 1;
    INSERT INTO @t VALUES (@IsPalindrome);
    RETURN
END
GO

TVF nội tuyến:

IF OBJECT_ID('dbo.InlineIsPalindrome') IS NOT NULL
DROP FUNCTION dbo.InlineIsPalindrome;
GO
CREATE FUNCTION dbo.InlineIsPalindrome
(
    @Word NVARCHAR(500)
)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
    WITH Nums AS
    (
      SELECT
        N = number
      FROM
        dbo.Numbers
    )
    SELECT
      IsPalindrome =
        CASE
          WHEN EXISTS
          (
            SELECT N
            FROM Nums
            WHERE N <= L / 2
              AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
          )
          THEN 0
          ELSE 1
        END
    FROM
      (SELECT LTRIM(RTRIM(@Word)), LEN(@Word)) AS v (S, L)
);
GO

Các Numbersbảng trong hàm trên được định nghĩa là:

CREATE TABLE dbo.Numbers
(
    Number INT NOT NULL 
);

Lưu ý: Bảng số không có bất kỳ chỉ mục nào và không có khóa chính và chứa 1.000.000 hàng.

Bàn thử giường tạm thời:

IF OBJECT_ID('tempdb.dbo.#Words') IS NOT NULL
DROP TABLE #Words;
GO
CREATE TABLE #Words 
(
    Word VARCHAR(500) NOT NULL
);

INSERT INTO #Words(Word) 
SELECT o.name + REVERSE(w.name)
FROM sys.objects o
CROSS APPLY (
    SELECT o.name
    FROM sys.objects o
) w;

Trên hệ thống kiểm tra của tôi ở trên INSERT kết quả trên có 16.900 hàng được chèn vào #Wordsbảng.

Để kiểm tra hai biến thể, tôi SET STATISTICS IO, TIME ON;và sử dụng như sau:

SELECT w.Word
    , p.IsPalindrome
FROM #Words w
    CROSS APPLY dbo.IsPalindrome(w.Word) p
ORDER BY w.Word;


SELECT w.Word
    , p.IsPalindrome
FROM #Words w
    CROSS APPLY dbo.InlineIsPalindrome(w.Word) p
ORDER BY w.Word;

Tôi mong đợi InlineIsPalindrome phiên bản sẽ nhanh hơn đáng kể, tuy nhiên các kết quả sau đây không hỗ trợ giả định đó.

TVF đa tuyên bố:

Bảng '# A1CE04C3'. Số lượng quét 16896, đọc logic 16900, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc trước 0.
Bảng 'Worktable'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Bảng '# र'. Quét số 1, đọc logic 88, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.

Thời gian thực thi của máy chủ SQL:
Thời gian CPU = 1700 ms, thời gian trôi qua = 2022 ms.
Thời gian phân tích và biên dịch SQL Server:
CPU time = 0 ms, thời gian trôi qua = 0 ms.

TVF nội tuyến:

Bảng 'Số'. Quét số 1, đọc logic 1272030, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc trước 0.
Bảng 'Bàn làm việc'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc đọc trước 0, đọc trước 0.
Bảng '# र'. Quét số 1, đọc logic 88, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.

Thời gian thực thi máy chủ SQL:
Thời gian CPU = 137874 ms, thời gian trôi qua = 139415 ms.
Thời gian phân tích và biên dịch SQL Server:
CPU time = 0 ms, thời gian trôi qua = 0 ms.

Các kế hoạch thực hiện trông như:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Tại sao biến thể nội tuyến chậm hơn nhiều so với biến thể đa câu lệnh, trong trường hợp này?

Đáp lại nhận xét của @AaronBertrand, tôi đã sửa đổi dbo.InlineIsPalindromehàm để giới hạn các hàng được CTE trả về để khớp với độ dài của từ đầu vào:

CREATE FUNCTION dbo.InlineIsPalindrome
(
    @Word NVARCHAR(500)
)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
    WITH Nums AS
    (
      SELECT
        N = number
      FROM
        dbo.Numbers
      WHERE 
        number <= LEN(@Word)
    )
    SELECT
      IsPalindrome =
        CASE
          WHEN EXISTS
          (
            SELECT N
            FROM Nums
            WHERE N <= L / 2
              AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
          )
          THEN 0
          ELSE 1
        END
    FROM
      (SELECT LTRIM(RTRIM(@Word)), LEN(@Word)) AS v (S, L)
);

Như @MartinSmith đã đề xuất, tôi đã thêm một khóa chính và chỉ mục cụm vào dbo.Numbersbảng, điều này chắc chắn sẽ giúp và sẽ gần hơn với những gì người ta sẽ thấy trong môi trường sản xuất.

Chạy lại các bài kiểm tra ở trên bây giờ dẫn đến các số liệu thống kê sau:

CROSS APPLY dbo.IsPalindrome(w.Word) p:

(17424 hàng bị ảnh hưởng)
Bảng '# B1104853'. Số lần quét 17420, đọc logic 17424, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc trước 0.
Bảng 'Bàn làm việc'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc đọc trước 0, đọc trước 0.
Bảng '# र'. Quét số 1, đọc logic 90, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.

Thời gian thực thi máy chủ SQL:
Thời gian CPU = 1763 ms, thời gian trôi qua = 2192 ms.

dbo.FunctionIsPalindrome(w.Word):

(17424 hàng bị ảnh hưởng)
Bảng 'Worktable'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc đọc trước 0, đọc trước 0.
Bảng '# र'. Quét số 1, đọc logic 90, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.

Thời gian thực thi của máy chủ SQL:
Thời gian CPU = 328 ms, thời gian đã trôi qua = 424 ms.

CROSS APPLY dbo.InlineIsPalindrome(w.Word) p:

(17424 hàng bị ảnh hưởng)
Bảng 'Số'. Quét số 1, đọc logic 237100, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc trước 0.
Bảng 'Bàn làm việc'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc đọc trước 0, đọc trước 0.
Bảng '# र'. Quét số 1, đọc logic 90, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.

Thời gian thực thi máy chủ SQL:
Thời gian CPU = 17737 ms, thời gian trôi qua = 17946 ms.

Tôi đang thử nghiệm điều này trên SQL Server 2012 SP3, v11.0.6020, Phiên bản dành cho nhà phát triển.

Đây là định nghĩa của bảng số của tôi, với khóa chính và chỉ mục được nhóm:

CREATE TABLE dbo.Numbers
(
    Number INT NOT NULL 
        CONSTRAINT PK_Numbers
        PRIMARY KEY CLUSTERED
);

;WITH n AS
(
    SELECT v.n 
    FROM (
        VALUES (1) 
            ,(2) 
            ,(3) 
            ,(4) 
            ,(5) 
            ,(6) 
            ,(7) 
            ,(8) 
            ,(9) 
            ,(10)
        ) v(n)
)
INSERT INTO dbo.Numbers(Number)
SELECT ROW_NUMBER() OVER (ORDER BY n1.n)
FROM n n1
    , n n2
    , n n3
    , n n4
    , n n5
    , n n6;

Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Paul White nói GoFundMonica

Câu trả lời:


12

Bảng số của bạn là một đống và có khả năng được quét hoàn toàn mỗi lần.

Thêm khóa chính được nhóm Numbervà thử các forceseekgợi ý sau với gợi ý để có được tìm kiếm mong muốn.

Theo như tôi có thể nói gợi ý này là cần thiết vì SQL Server chỉ ước tính rằng 27% của bảng sẽ khớp với vị từ (30% cho <=và giảm xuống còn 27% theo <>). Và do đó, nó sẽ chỉ phải đọc 3-4 hàng trước khi tìm thấy một hàng phù hợp và nó có thể thoát khỏi bán tham gia. Vì vậy, tùy chọn quét được chi phí rất rẻ. Nhưng trên thực tế nếu có bất kỳ palindromes nào tồn tại thì nó sẽ phải đọc toàn bộ bảng nên đây không phải là một kế hoạch tốt.

CREATE FUNCTION dbo.InlineIsPalindrome
(
    @Word NVARCHAR(500)
)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
    WITH Nums AS
    (
      SELECT
        N = number
      FROM
        dbo.Numbers WITH(FORCESEEK)
    )
    SELECT
      IsPalindrome =
        CASE
          WHEN EXISTS
          (
            SELECT N
            FROM Nums
            WHERE N <= L / 2
              AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
          )
          THEN 0
          ELSE 1
        END
    FROM
      (SELECT LTRIM(RTRIM(@Word)), LEN(@Word)) AS v (S, L)
);
GO

Với những thay đổi tại chỗ, nó bay theo tôi (mất 228ms)

nhập mô tả hình ảnh ở đây

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.