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 Numbers
bả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 #Words
bả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ư:
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.InlineIsPalindrome
hà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.Numbers
bả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;