मल्टी स्टेटमेंट टीवीएफ बनाम इनलाइन टीवीएफ प्रदर्शन


18

पैलिंड्रोम प्रश्न पर कुछ उत्तरों की तुलना (10k + उपयोगकर्ता केवल इसलिए, क्योंकि मैंने उत्तर हटा दिया है), मुझे भ्रमित करने वाले परिणाम मिल रहे हैं।

मैंने एक मल्टी-स्टेटमेंट, स्कीमा-बाउंड टीवीएफ प्रस्तावित किया था जो मुझे लगा था कि यह एक मानक फ़ंक्शन चलाने से तेज होगा, जो कि यह है। मैं इस धारणा के तहत था कि मल्टी-स्टेटमेंट टीवीएफ "इनबिल्ड" होगा, हालांकि मैं उस गिनती पर गलत हूं, जैसा कि आप नीचे देखेंगे। यह सवाल टीवीएफ की उन दो शैलियों के प्रदर्शन अंतर के बारे में है। सबसे पहले, आपको कोड देखना होगा।

यहाँ बहु-कथन 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

इनलाइन-टीवीएफ:

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परिणाम 16,900 पंक्तियों को #Wordsतालिका में डाला गया है ।

दो भिन्नताओं का परीक्षण करने के लिए, मैं 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, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0.
टेबल 'वर्कटेबल'। स्कैन काउंट 0, लॉजिकल
रीड्स 0, फिजिकल रीड्स 0, रीड- फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड- फॉरवर्ड रीड्स 0. टेबल '#स्क्रिप्ट'। स्कैन काउंट 1, लॉजिकल रीड 88, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0।

SQL सर्वर निष्पादन समय:
CPU समय = 1700 ms, बीता समय = 2022 ms।
SQL सर्वर पार्स और संकलित समय:
सीपीयू समय = 0 एमएस, बीता हुआ समय = 0 एमएस।

इनलाइन TVF:

तालिका 'संख्या'। स्कैन काउंट 1, लॉजिकल रीड 1272030, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लोब रीड-फॉरवर्ड रीड्स 0.
टेबल 'वर्कटेबल'। स्कैन काउंट 0, लॉजिकल
रीड्स 0, फिजिकल रीड्स 0, रीड- फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड- फॉरवर्ड रीड्स 0. टेबल '#स्क्रिप्ट'। स्कैन काउंट 1, लॉजिकल रीड 88, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0।

SQL सर्वर निष्पादन समय:
CPU समय = 137874 ms, बीता समय = 139415 ms।
SQL सर्वर पार्स और संकलित समय:
सीपीयू समय = 0 एमएस, बीता हुआ समय = 0 एमएस।

निष्पादन योजनाएं इस तरह दिखती हैं:

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

इनलाइन वेरिएंट इस मामले में मल्टी-स्टेटमेंट वेरिएंट से इतना धीमा क्यों है?

@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, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0.
टेबल 'वर्कटेबल'। स्कैन काउंट 0, लॉजिकल
रीड्स 0, फिजिकल रीड्स 0, रीड- फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड- फॉरवर्ड रीड्स 0. टेबल '#स्क्रिप्ट'। स्कैन काउंट 1, लॉजिकल रीड्स 90, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0।

SQL सर्वर निष्पादन समय:
CPU समय = 1763 एमएस, बीता हुआ समय = 2192 एमएस।

dbo.FunctionIsPalindrome(w.Word):

(17424 पंक्ति) प्रभावित
'टेबल' वर्कटेबल । स्कैन काउंट 0, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0।
रीड्स फिजिकल रीड्स फॉरवर्ड रीड्स रीड्स रीड्स फॉरवर्ड रीड्स टेबल '#स्क्रिप्ट'। स्कैन काउंट 1, लॉजिकल रीड्स 90, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0।

SQL सर्वर निष्पादन समय:
CPU समय = 328 ms, बीता समय = 424 ms।

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

(१ Numbers४२४ पंक्ति) प्रभावित
'तालिका' संख्याएँ। स्कैन काउंट 1, लॉजिकल रीड 237100, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0.
टेबल 'वर्कटेबल'। स्कैन काउंट 0, लॉजिकल
रीड्स 0, फिजिकल रीड्स 0, रीड- फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड- फॉरवर्ड रीड्स 0. टेबल '#स्क्रिप्ट'। स्कैन काउंट 1, लॉजिकल रीड्स 90, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0, लॉब लॉजिकल रीड्स 0, लॉब फिजिकल रीड्स 0, लॉब रीड-फॉरवर्ड रीड्स 0।

SQL सर्वर निष्पादन समय:
CPU समय = 17737 ms, बीता समय = 17946 ms।

मैं SQL Server 2012 SP3, v11.0.6020, डेवलपर संस्करण पर यह परीक्षण कर रहा हूं।

यहां प्राथमिक कुंजी और क्लस्टर इंडेक्स के साथ मेरी संख्या तालिका की परिभाषा दी गई है:

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;

टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
पॉल व्हाइट को फिर से बहाल मोनिका

जवाबों:


12

आपकी संख्या तालिका एक ढेर है और संभवतः हर बार पूरी तरह से स्कैन की जा रही है।

वांछित प्राथमिक कुंजी प्राप्त करने के लिए एक संकुल प्राथमिक कुंजी जोड़ें Numberऔर forceseekसंकेत के साथ निम्नलिखित का प्रयास करें।

जहां तक ​​मैं बता सकता हूं कि यह संकेत की जरूरत है क्योंकि SQL सर्वर का अनुमान है कि तालिका का 27% विधेय से मेल खाएगा (30% के लिए <=और घटाकर 27% तक <>)। और इसलिए कि यह केवल एक मैच खोजने से पहले 3-4 पंक्तियों को पढ़ना होगा और यह सेमी जॉइन से बाहर निकल सकता है। इसलिए स्कैन विकल्प को बहुत सस्ते में खर्च किया जाता है। लेकिन वास्तव में यदि कोई तालमेल मौजूद है, तो उसे पूरी तालिका को पढ़ना होगा, इसलिए यह एक अच्छी योजना नहीं है।

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

जगह में उन परिवर्तनों के साथ यह मेरे लिए उड़ता है (228ms लेता है)

यहाँ छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.