मल्टी-स्टेटमेंट टेबल वेल्यूड फंक्शन बनाम इनलाइन टेबल वैल्यूड फंक्शन


198

दिखाने के लिए कुछ उदाहरण, बस उकसाना:

इनलाइन टेबल मान्य

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

मल्टी स्टेटमेंट टेबल मान्य

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

क्या एक प्रकार (दूसरे में लाइन या मल्टी स्टेटमेंट) का उपयोग करने का कोई फायदा है? क्या कुछ निश्चित परिदृश्य हैं जब एक दूसरे से बेहतर है या अंतर विशुद्ध रूप से वाक्यात्मक हैं? मुझे लगता है कि दो उदाहरण प्रश्न अलग-अलग चीजें कर रहे हैं, लेकिन क्या कोई कारण है कि मैं उन्हें इस तरह से लिखूंगा?

उनके बारे में पढ़ना और फायदे / मतभेद वास्तव में नहीं बताए गए हैं।


इनलाइन फ़ंक्शन का एक बड़ा लाभ यह है कि आप ROWID (TIMESTAMP) कॉलम का चयन कर सकते हैं, जबकि आप मल्टीस्टैटमेंट फ़ंक्शन में TIMESTAMP डेटा को रिटर्न टेबल पर सम्मिलित नहीं कर सकते हैं!
आर्ट्रू

3
एक उत्कृष्ट सूत्र के लिए धन्यवाद। मैंने बहुत कुछ सीखा है। हालाँकि, एक बात का ध्यान रखें जब एक फ़ंक्शन जो कि ITV से MSTV था, तो प्रोफाइलर को लगता है कि आप ITV बदल रहे हैं। कोई फर्क नहीं पड़ता कि आप एक MSTV बिंदु से वाक्यविन्यास अधिकार प्राप्त करने के लिए क्या करते हैं, recompile हमेशा विफल रहता है, आमतौर पर BEGIN के बाद पहले कथन के आसपास। इसके चारों ओर एकमात्र तरीका पुराने कार्य को रोकना और नए को MSTV के रूप में बनाना था।
फान्डैंगो 68

जवाबों:


141

मैट की टिप्पणी पर शोध करने में, मैंने अपने मूल कथन को संशोधित किया है। वह सही है, इनलाइन टेबल वैल्यू फंक्शन (ITVF) और मल्टी-स्टेटमेंट टेबल वैल्यू फंक्शन (MSTVF) के बीच प्रदर्शन में अंतर होगा, भले ही वे दोनों केवल एक सेलेक्ट स्टेटमेंट को अंजाम दें। SQL सर्वर एक ITVF को कुछ हद तक एक की तरह व्यवहार करेगाVIEWइसमें वह विचाराधीन तालिकाओं के नवीनतम आंकड़ों का उपयोग करके एक निष्पादन योजना की गणना करेगा। एक MSTVF आपके SELECT स्टेटमेंट की पूरी सामग्री को टेबल वैरिएबल में स्टफ करने और फिर उससे जुड़ने के बराबर है। इस प्रकार, कंपाइलर MSTVF में तालिकाओं पर किसी भी तालिका आँकड़ों का उपयोग नहीं कर सकता है। इसलिए, सभी चीजें समान हैं, (जो कि वे शायद ही कभी हैं), ITVF MSTVF से बेहतर प्रदर्शन करेंगे। मेरे परीक्षणों में, एक सांख्यिकी दृष्टिकोण के अनुसार, समय पूरा करने में प्रदर्शन अंतर नगण्य था, यह ध्यान देने योग्य था।

आपके मामले में, दो कार्य कार्यात्मक रूप से समतुल्य नहीं हैं। MSTV फ़ंक्शन कॉल किए जाने पर प्रत्येक बार एक अतिरिक्त क्वेरी करता है और सबसे महत्वपूर्ण बात, ग्राहक आईडी पर फ़िल्टर करता है। एक बड़ी क्वेरी में, ऑप्टिमाइज़र अन्य प्रकार के जॉन्स का लाभ नहीं ले पाएगा क्योंकि इसे प्रत्येक ग्राहक के लिए फ़ंक्शन को कॉल करने की आवश्यकता होगी जो पारित हो गया है। हालाँकि, यदि आपने अपने MSTV फ़ंक्शन को फिर से लिखा है तो:

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

क्वेरी में, ऑप्टिमाइज़र उस फ़ंक्शन को एक बार कॉल करने और बेहतर निष्पादन योजना बनाने में सक्षम होगा, लेकिन यह अभी भी एक समकक्ष, गैर-पैरामीटर वाले आईटीवीएस या ए से बेहतर नहीं होगा VIEW

संभव होने पर एक MSTVFs पर ITVF को प्राथमिकता दी जानी चाहिए क्योंकि तालिका में कॉलम से डेटाटिप्स, अशक्तता और टकराव, जबकि आप मल्टी-स्टेटमेंट टेबल वैल्यू फ़ंक्शन में उन गुणों की घोषणा करते हैं और, महत्वपूर्ण बात, आपको ITVF से बेहतर निष्पादन योजनाएं मिलेंगी। मेरे अनुभव में, मुझे ऐसी कई परिस्थितियाँ नहीं मिली हैं जहाँ एक ITVF एक VIEW से बेहतर विकल्प था, लेकिन माइलेज भिन्न हो सकती है।

मैट का शुक्रिया।

इसके अलावा

जब से मैंने हाल ही में इसे देखा है, यहाँ इनलाइन शेप्ड फ़ंक्शन और मल्टी-स्टेटमेंट फ़ंक्शंस के बीच प्रदर्शन अंतर की तुलना करके वेन शेफ़ील्ड द्वारा किया गया एक उत्कृष्ट विश्लेषण है।

उनका मूल ब्लॉग पोस्ट।

SQL सर्वर सेंट्रल पर कॉपी करें


40
यह केवल सच नहीं है - मल्टी-स्टेटमेंट फ़ंक्शन बहुत बार बहुत बड़ा प्रदर्शन होता है क्योंकि वे क्वेरी ऑप्टिमाइज़र को आंकड़ों का उपयोग करने से रोकते हैं। अगर मेरे पास हर बार $ 1 था, तो मैंने मल्टी-स्टेटमेंट फंक्शन का उपयोग निष्पादन योजना के बहुत खराब विकल्प का कारण बना (ज्यादातर क्योंकि यह आमतौर पर 1 के रूप में लौटी हुई पंक्ति की संख्या का अनुमान लगाता है), मेरे पास एक छोटी कार खरीदने के लिए पर्याप्त है।
मैट व्हिटफील्ड

सबसे अच्छा स्पष्टीकरण जो मैंने कभी पाया है, वह पहले उत्तर में है, और संबंधित पोस्ट: stackoverflow.com/questions/4109152/… तेह संबंधित दस्तावेज़ को याद न करें, आप इसे जल्दी से पढ़ सकते हैं, और यह बहुत दिलचस्प है।
जटबे

1
SQL सर्वर 2017 के लिए इस उत्तर के लिए एक अद्यतन होगा ?: youtube.com/watch?time_continue=2&v=szTmo6rTUjM
राल्फ

29

आंतरिक रूप से, SQL सर्वर एक इनलाइन टेबल वैल्यू फ़ंक्शन को बहुत पसंद करता है, जैसे कि यह एक दृश्य होगा और एक बहु-स्टेटमेंट टेबल वैल्यू फ़ंक्शन के समान व्यवहार करता है कि यह कैसे एक संग्रहीत कार्यविधि है।

जब एक इनलाइन तालिका-मूल्यवान फ़ंक्शन का उपयोग बाहरी क्वेरी के हिस्से के रूप में किया जाता है, तो क्वेरी प्रोसेसर UDF परिभाषा का विस्तार करता है और इन वस्तुओं पर अनुक्रमित का उपयोग करते हुए एक निष्पादन योजना बनाता है जो अंतर्निहित वस्तुओं तक पहुंचता है।

मल्टी-स्टेटमेंट टेबल वैल्यू फंक्शन के लिए, फंक्शन के लिए एक एक्जीक्यूशन प्लान बनाया जाता है और एक्जीक्यूटिव प्लान कैश में स्टोर किया जाता है (एक बार फंक्शन को पहली बार निष्पादित किया गया है)। यदि मल्टी-स्टेटमेंट टेबल वैल्यू फ़ंक्शंस का उपयोग बड़े प्रश्नों के हिस्से के रूप में किया जाता है, तो ऑप्टिमाइज़र को यह पता नहीं होता है कि फ़ंक्शन क्या देता है, और इसलिए कुछ मानक धारणाएं बनाता है - वास्तव में यह मानता है कि फ़ंक्शन एक पंक्ति में लौटेगा, और यह कि रिटर्न की जानकारी फ़ंक्शन को एकल पंक्ति के साथ तालिका के विरुद्ध टेबल स्कैन का उपयोग करके एक्सेस किया जाएगा।

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

अंगूठे के एक सामान्य नियम के रूप में हमने पाया है कि इन संभावित प्रदर्शन समस्याओं के कारण जहां संभव इनलाइन टेबल वैल्यू फ़ंक्शन का उपयोग मल्टी स्टेटमेंट वालों (जब यूडीएफ को बाहरी क्वेरी के हिस्से के रूप में किया जाएगा) के लिए प्राथमिकता में किया जाना चाहिए।


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

6
जब तक आपको उन परिणामों को किसी अन्य क्वेरी में शामिल करने की आवश्यकता नहीं है।
गुइलेर्मो गुतिरेज़

दोनों का उपयोग क्यों नहीं? एक संग्रहित खरीद जो एक बहु-कथन तालिका-मूल्यवान फ़ंक्शन का परिणाम देता है। दोनों ओर से लाभदायक।
रोबिनो

13

एक और अंतर है। इनलाइन टेबल-वैल्यू फ़ंक्शन को एक दृश्य की तरह, अपडेट, और डिलीट किया जा सकता है। इसी तरह के प्रतिबंध लागू होते हैं - एग्रीगेट का उपयोग करके फ़ंक्शंस को अपडेट नहीं कर सकते हैं, परिकलित कॉलम को अपडेट नहीं कर सकते हैं, और इसी तरह।


3

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

मेरा सुझाव है कि जब भी संभव हो, सबसे सरल (इनलाइन) का उपयोग करें और जब आवश्यक हो (स्पष्ट रूप से) या जब व्यक्तिगत वरीयता / पठनीयता का उपयोग करते हैं तो यह अतिरिक्त टाइपिंग को समाप्त कर देता है।


जवाब के लिए धन्यवाद। इसलिए मूल रूप से, बहु-कथन का उपयोग केवल तब किया जाता है जब फ़ंक्शन इनलाइन फ़ंक्शन की पठनीयता के लिए इनलाइन फ़ंक्शन की तुलना में अधिक जटिल होता है? क्या बहु-कथन के लिए कोई प्रदर्शन लाभ हैं?
एंड्रयूज

मुझे नहीं पता, लेकिन मुझे ऐसा नहीं लगता। संभवतः sql सर्वर को उन ऑप्टिमाइज़ेशन का पता लगाने देना बेहतर है जिन्हें आप मैन्युअल रूप से बनाने की कोशिश कर सकते हैं (चर, अस्थायी तालिकाओं या जो कुछ भी उपयोग करके)। यद्यपि आप निश्चित रूप से कुछ मामलों में इसे साबित / अस्वीकृत करने के लिए कुछ प्रदर्शन परीक्षण कर सकते हैं।
रे

बहुत धन्यवाद फिर से। अधिक समय होने पर मैं इस पर और गौर कर सकता हूं! :)
एंड्रयूज


0

मैंने इसका परीक्षण नहीं किया है, लेकिन एक मल्टी स्टेटमेंट फ़ंक्शन परिणाम सेट को कैश करता है। ऐसे मामले हो सकते हैं, जहां फ़ंक्शन को इनलाइन करने के लिए ऑप्टिमाइज़र के लिए बहुत अधिक चल रहा है। उदाहरण के लिए मान लीजिए कि आपके पास एक ऐसा फ़ंक्शन है जो "डेटाबेस नंबर" के रूप में आपके द्वारा पास किए जाने के आधार पर विभिन्न डेटाबेस से एक परिणाम देता है। आम तौर पर, आप एक यूनियन के साथ एक दृश्य बना सकते हैं, फिर कंपनी नंबर से फ़िल्टर कर सकते हैं, लेकिन मैंने पाया कि कभी-कभी sql सर्वर पूरे यूनियन को वापस खींच लेता है और एक चयन को कॉल करने के लिए पर्याप्त स्मार्ट नहीं है। स्रोत को चुनने के लिए तालिका फ़ंक्शन में तर्क हो सकते हैं।


0

मल्टी लाइन फ़ंक्शन का उपयोग करने के लिए एक अन्य मामला एसक्यूएल सर्वर को जहां क्लॉज को नीचे धकेलने से रोकने के लिए होगा।

उदाहरण के लिए, मेरे पास एक टेबल का नाम है और कुछ टेबल के नाम C05_2019 और C12_2018 जैसे फॉर्मेट किए गए हैं और सभी टेबल फॉर्मेट किए गए हैं जिस तरह से एक ही स्कीमा है। मैं उस सभी डेटा को एक टेबल में मर्ज करना चाहता था और 05 और 12 को एक कंपोनो कॉलम और 2018,2019 को एक साल के कॉलम में पार्स कर दिया। हालाँकि, ACA_StupidTable जैसी अन्य तालिकाएँ हैं, जिन्हें मैं CompNo और CompYr नहीं निकाल सकता और यदि मैंने कोशिश की तो रूपांतरण रूपांतरण प्राप्त होगा। तो, मेरी क्वेरी दो भाग में थी, एक आंतरिक क्वेरी जो केवल 'C_______' की तरह स्वरूपित टेबल पर लौटी थी, फिर बाहरी क्वेरी ने एक उप-स्ट्रिंग और इंट रूपांतरण किया। यानी कास्ट (सब्स्टिट्यूट (2, 2) इंट के रूप में) कोम्पो के रूप में। सभी अच्छा लग रहा है सिवाय इसके कि sql सर्वर ने परिणामों को फ़िल्टर करने से पहले मेरे कास्ट फ़ंक्शन को डालने का फैसला किया और इसलिए मुझे रूपांतरण त्रुटि का मन मिल गया। एक मल्टी स्टेटमेंट टेबल फ़ंक्शन ऐसा होने से रोक सकता है,


0

शायद बहुत ही गाढ़े तरीके से। ITVF (इनलाइन TVF): यदि आप DB व्यक्ति हैं, तो अधिक पैरामीटर वाला दृश्य है, एकल चयन सेंट लें

MTVF (मल्टी-स्टेटमेंट TVF): डेवलपर, टेबल वेरिएबल बनाता और लोड करता है।


-2

यदि आप एक क्वेरी करने जा रहे हैं तो आप अपने इनलाइन टेबल वेल्यूड फंक्शन में शामिल हो सकते हैं:

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

यह थोड़ा उपरिव्यय और ठीक चला जाएगा।

यदि आप अपने बहु स्टेटमेंट टेबल को एक समान क्वेरी में उपयोग करने का प्रयास करते हैं, तो आपके पास प्रदर्शन संबंधी समस्याएं होंगी:

SELECT
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty
    FROM xxxx   x

क्योंकि आप फ़ंक्शन को प्रत्येक पंक्ति के लिए 1 बार लौटाएंगे, क्योंकि परिणाम सेट बड़ा हो जाता है, यह धीमा और धीमा चलेगा।


आह, तो आप कहेंगे कि प्रदर्शन के मामले में इनलाइन बेहतर है?
एंड्रयूज

1
नहीं, वे दोनों एक तालिका लौटाते हैं, जो आपकी दूसरी SQL को अमान्य बनाता है क्योंकि आप एक स्तंभ में एक तालिका डालने की कोशिश कर रहे हैं।
cjk

1
@ck, मैंने क्वेरी को अपडेट किया है जिस पर यो ने टिप्पणी की है। दूसरे फ़ंक्शन में उपयोग किए जाने वाले फ़ंक्शन के पैरामीटर इसे एक उप क्वेरी के रूप में उपयोग करने के लिए उधार देते हैं, जिसके परिणामस्वरूप खराब प्रदर्शन होगा।
के.एम.
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.