TVF ที่มีหลายงบและประสิทธิภาพแบบอินไลน์ TVF


18

การเปรียบเทียบคำตอบบางส่วนกับคำถาม Palindrome (เฉพาะผู้ใช้ 10k + เท่านั้นเนื่องจากฉันได้ลบคำตอบแล้ว) ฉันจึงได้ผลลัพธ์ที่สับสน

ฉันเสนอTVFซึ่งมีหลายสคีมาซึ่งฉันคิดว่าน่าจะเร็วกว่าการใช้งานฟังก์ชั่นมาตรฐานซึ่งเป็น ฉันยังอยู่ภายใต้ความประทับใจที่ TVF หลายงบจะ "อินไลน์" แม้ว่าฉันจะผิดในการนับที่คุณจะเห็นด้านล่าง คำถามนี้เกี่ยวกับความแตกต่างด้านประสิทธิภาพของ TVF ทั้งสองรูปแบบ ก่อนอื่นคุณจะต้องเห็นรหัส

นี่คือ TVF หลายงบ:

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

Inline-TVF:

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

Numbersตารางในฟังก์ชั่นดังกล่าวข้างต้นหมายถึง:

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

หมายเหตุ: ตารางตัวเลขไม่มีดัชนีและไม่มีคีย์หลักและมี 1,000,000 แถว

ตารางชั่วคราวสำหรับทดสอบเตียง:

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;

ในระบบทดสอบของฉันINSERTผลลัพธ์ด้านบนมีการแทรกแถวลงใน#Wordsตาราง16,900

ในการทดสอบทั้งสองรูปแบบฉันSET STATISTICS IO, TIME ON;และใช้สิ่งต่อไปนี้:

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;

ฉันคาดว่าInlineIsPalindromeรุ่นนี้จะเร็วกว่าอย่างมาก แต่ผลลัพธ์ต่อไปนี้ไม่สนับสนุนการคาดคะเนนั้น

หลายงบ TVF:

ตาราง '# A1CE04C3' จำนวนการสแกน 16896, ตรรกะอ่าน 16900, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob การอ่านตรรกะ 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0
ตาราง 'โต๊ะทำงาน' จำนวนการสแกน 0, อ่านโลจิคัล 0, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, ลอจิคัลอ่าน 0, ลูกเทนนิสอ่าน 0,
ล็อคอ่านล่วงหน้าอ่าน 0 ตาราง '#Words' จำนวนการสแกน 1, การอ่านตรรกะ 88, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob ตรรกะอ่าน 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ sql server:
เวลา CPU = 1700 ms, เวลาที่ผ่านไป = 2022 ms
การแยกวิเคราะห์และเวลาในการรวบรวมของ SQL Server: เวลา
CPU = 0 ms, เวลาที่ผ่านไป = 0 ms

Inline TVF:

ตาราง 'ตัวเลข' จำนวนการสแกน 1, ตรรกะอ่าน 1272030, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, การอ่านตรรกะล่วงหน้า lob 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0
ตาราง 'โต๊ะทำงาน' จำนวนการสแกน 0, อ่านโลจิคัล 0, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, ลอจิคัลอ่าน 0, ลูกเทนนิสอ่าน 0,
ล็อคอ่านล่วงหน้าอ่าน 0 ตาราง '#Words' จำนวนการสแกน 1, การอ่านตรรกะ 88, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob ตรรกะอ่าน 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server:
เวลา CPU = 137874 ms, เวลาที่ผ่านไป = 139415 ms
การแยกวิเคราะห์และเวลาในการรวบรวมของ SQL Server: เวลา
CPU = 0 ms, เวลาที่ผ่านไป = 0 ms

แผนการดำเนินการมีลักษณะดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

เหตุใดตัวแปรแบบอินไลน์จึงช้ากว่าชุดคำสั่งหลายคำสั่งมากในกรณีนี้

ในการตอบกลับความคิดเห็นโดย @AaronBertrand ฉันได้แก้ไขdbo.InlineIsPalindromeฟังก์ชันเพื่อ จำกัด แถวที่ส่งคืนโดย CTE เพื่อให้ตรงกับความยาวของคำที่ป้อน:

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

ตามที่ @MartinSmith แนะนำฉันได้เพิ่มคีย์หลักและดัชนีคลัสเตอร์ลงในdbo.Numbersตารางซึ่งช่วยได้อย่างแน่นอนและจะใกล้เคียงกับสิ่งที่คาดว่าจะเห็นในสภาพแวดล้อมการผลิต

การเรียกใช้การทดสอบข้างต้นใหม่จะส่งผลให้เกิดสถิติต่อไปนี้:

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

(17424 แถวที่ได้รับผลกระทบ)
ตาราง '# B1104853' จำนวนการสแกน 17420, การอ่านตรรกะ 17424, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, การอ่านตรรกะล่วงหน้า lob 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0
ตาราง 'โต๊ะทำงาน' จำนวนการสแกน 0, อ่านโลจิคัล 0, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, ลอจิคัลอ่าน 0, ลูกเทนนิสอ่าน 0,
ล็อคอ่านล่วงหน้าอ่าน 0 ตาราง '#Words' จำนวนการสแกน 1, อ่านโลจิคัล 90, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, lob อ่านโลจิคัล 0, อ่านทางกายภาพ lob 0, lob อ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server:
เวลา CPU = 1763 ms, เวลาที่ผ่านไป = 2192 ms

dbo.FunctionIsPalindrome(w.Word):

(17424 แถวที่ได้รับผลกระทบ)
ตาราง 'Worktable' จำนวนการสแกน 0, อ่านโลจิคัล 0, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, ลอจิคัลอ่าน 0, ลูกเทนนิสอ่าน 0,
ล็อคอ่านล่วงหน้าอ่าน 0 ตาราง '#Words' จำนวนการสแกน 1, อ่านโลจิคัล 90, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, lob อ่านโลจิคัล 0, อ่านทางกายภาพ lob 0, lob อ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server:
เวลา CPU = 328 ms, เวลาที่ผ่านไป = 424 ms

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

(17424 แถวที่ได้รับผลกระทบ)
ตาราง 'หมายเลข' จำนวนการสแกน 1, ตรรกะอ่าน 237100, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob ตรรกะอ่าน 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0
ตาราง 'โต๊ะทำงาน' จำนวนการสแกน 0, อ่านโลจิคัล 0, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, ลอจิคัลอ่าน 0, ลูกเทนนิสอ่าน 0,
ล็อคอ่านล่วงหน้าอ่าน 0 ตาราง '#Words' จำนวนการสแกน 1, อ่านโลจิคัล 90, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, lob อ่านโลจิคัล 0, อ่านทางกายภาพ lob 0, lob อ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server:
เวลา CPU = 17737 ms, เวลาที่ผ่านไป = 17946 ms

ฉันกำลังทดสอบสิ่งนี้ใน SQL Server 2012 SP3, v11.0.6020, Developer Edition

นี่คือคำจำกัดความของตารางตัวเลขของฉันโดยมีคีย์หลักและดัชนีคลัสเตอร์:

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;

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
พอลไวท์พูดว่า GoFundMonica

คำตอบ:


12

ตารางตัวเลขของคุณเป็นจำนวนมากและอาจถูกสแกนอย่างสมบูรณ์ในแต่ละครั้ง

เพิ่มคีย์หลักในคลัสเตอร์Numberและลองทำตามforceseekคำแนะนำเพื่อค้นหาสิ่งที่ต้องการ

เท่าที่ฉันสามารถบอกได้ว่าจำเป็นต้องใช้คำแนะนำนี้เนื่องจาก SQL Server เพียงประมาณ 27% ของตารางจะตรงกับภาคแสดง (30% สำหรับ<=และลดลงเหลือ 27% โดย<>) และดังนั้นจึงจะต้องอ่าน 3-4 แถวก่อนที่จะค้นหาหนึ่งที่ตรงกันและสามารถออกจากการเข้าร่วมกึ่ง ดังนั้นตัวเลือกการสแกนมีราคาถูกมาก แต่ในความเป็นจริงถ้ามี palindromes อยู่แล้วมันจะต้องอ่านทั้งตารางดังนั้นนี่ไม่ใช่แผนที่ดี

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

เมื่อมีการเปลี่ยนแปลงเหล่านั้นทำให้ฉันบินได้ (ใช้เวลา 228 มิลลิวินาที)

ป้อนคำอธิบายรูปภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.