पिछले हफ्ते, मैंने नए STRING_AGG()
को शामिल करते हुए कुछ त्वरित प्रदर्शन तुलनाएं कीं पारंपरिक FOR XML PATH
के खिलाफ कार्य करें दृष्टिकोण जो मैंने उम्र के लिए उपयोग किया है। मैंने अपरिभाषित/मनमानी क्रम के साथ-साथ स्पष्ट आदेश और STRING_AGG()
दोनों का परीक्षण किया दोनों ही मामलों में शीर्ष पर आए:
- SQL सर्वर v.अगला :STRING_AGG() प्रदर्शन, भाग 1
उन परीक्षणों के लिए, मैंने कई चीजें छोड़ दीं (सभी जानबूझकर नहीं):
- मिकेल एरिक्सन और ग्रेज़गोर्ज़ yp दोनों ने बताया कि मैं पूर्णतम सबसे कुशल
FOR XML PATH
का उपयोग नहीं कर रहा था निर्माण (और स्पष्ट होने के लिए, मेरे पास कभी नहीं है)। - मैंने Linux पर कोई परीक्षण नहीं किया; केवल विंडोज़ पर। मुझे उम्मीद नहीं है कि वे बहुत अलग होंगे, लेकिन चूंकि ग्रेज़गोरज़ ने बहुत अलग अवधि देखी, यह आगे की जांच के लायक है।
- मैंने भी केवल तभी परीक्षण किया जब आउटपुट एक सीमित, गैर-एलओबी स्ट्रिंग होगा - जो मुझे लगता है कि यह सबसे आम उपयोग का मामला है (मुझे नहीं लगता कि लोग आम तौर पर एक तालिका में प्रत्येक पंक्ति को एक अल्पविराम से अलग कर देंगे स्ट्रिंग, लेकिन यही कारण है कि मैंने अपनी पिछली पोस्ट में आपके उपयोग के मामलों के लिए कहा था)।
- आदेश परीक्षण के लिए, मैंने कोई अनुक्रमणिका नहीं बनाई जो सहायक हो सकती है (या कुछ भी कोशिश करें जहां सभी डेटा एक ही तालिका से आए)।
इस पोस्ट में, मैं इनमें से कुछ वस्तुओं से निपटने जा रहा हूँ, लेकिन उन सभी पर नहीं।
एक्सएमएल पथ के लिए
मैं निम्नलिखित का उपयोग कर रहा था:
... XML PATH के लिए, TYPE).value(N'.[1]', ...
मिकेल की इस टिप्पणी के बाद, मैंने इसके बजाय इस थोड़े अलग निर्माण का उपयोग करने के लिए अपना कोड अपडेट किया है:
... XML PATH(''), TYPE) के लिए। value(N'text()[1]', ...
लिनक्स बनाम विंडोज़
प्रारंभ में, मैंने केवल विंडोज़ पर परीक्षण चलाने की जहमत उठाई थी:
Microsoft SQL Server vNext (CTP1.1) - 14.0.100.187 (X64) Dec 10 2016 02:51:11 कॉपीराइट (C) 2016 Microsoft Corporation। सर्वाधिकार सुरक्षित। विंडोज सर्वर 2016 डाटासेंटर 6.3 पर डेवलपर संस्करण (64-बिट)(बिल्ड 14393:) (हाइपरवाइजर)
लेकिन ग्रेज़गोर्ज़ ने एक उचित बिंदु बनाया कि वह (और संभवतः कई अन्य) केवल सीटीपी 1.1 के लिनक्स स्वाद तक पहुंच प्राप्त कर सकते थे। इसलिए मैंने अपने परीक्षण मैट्रिक्स में Linux जोड़ा:
Microsoft SQL Server vNext (CTP1.1) - 14.0.100.187 (X64) Dec 10 2016 02:51:11 कॉपीराइट (C) 2016 Microsoft Corporation। सर्वाधिकार सुरक्षित। लिनक्स पर (उबंटू 16.04.1 एलटीएस)
कुछ दिलचस्प लेकिन पूरी तरह से स्पर्शरेखा अवलोकन:
@@VERSION
इस बिल्ड में संस्करण नहीं दिखाता है, लेकिनSERVERPROPERTY('Edition')
अपेक्षितDeveloper Edition (64-bit)
देता है ।- बाइनरी में एन्कोड किए गए बिल्ड समय के आधार पर, विंडोज और लिनक्स संस्करण अब एक ही समय और एक ही स्रोत से संकलित किए गए प्रतीत होते हैं। या यह एक पागल संयोग था।
अनियंत्रित परीक्षण
मैंने मनमाने ढंग से आदेशित आउटपुट का परीक्षण करके शुरू किया (जहां समेकित मूल्यों के लिए स्पष्ट रूप से परिभाषित आदेश नहीं है)। ग्रेज़गोर्ज़ के बाद, मैंने वाइडवर्ल्ड इम्पोर्टर्स (स्टैंडर्ड) का उपयोग किया, लेकिन Sales.Orders
के बीच एक जॉइन किया। और Sales.OrderLines
. यहां काल्पनिक आवश्यकता सभी आदेशों की एक सूची को आउटपुट करना है, और प्रत्येक आदेश के साथ, प्रत्येक StockItemID
की अल्पविराम से अलग की गई सूची है। .
चूंकि StockItemID
एक पूर्णांक है, हम एक परिभाषित varchar
. का उपयोग कर सकते हैं , जिसका अर्थ है कि MAX की आवश्यकता के बारे में चिंता करने से पहले स्ट्रिंग 8000 वर्णों की हो सकती है। चूंकि एक इंट अधिकतम लंबाई 11 (वास्तव में 10, अगर अहस्ताक्षरित) हो सकता है, साथ ही अल्पविराम, इसका मतलब है कि ऑर्डर को सबसे खराब स्थिति में लगभग 8,000/12 (666) स्टॉक आइटम का समर्थन करना होगा (उदाहरण के लिए सभी StockItemID मानों में है 11 अंक)। हमारे मामले में, सबसे लंबी आईडी 3 अंक है, इसलिए जब तक डेटा जोड़ा नहीं जाता है, हमें वास्तव में MAX को सही ठहराने के लिए किसी एक क्रम में 8,000/4 (2,000) अद्वितीय स्टॉक आइटम की आवश्यकता होगी। हमारे मामले में, कुल मिलाकर केवल 227 स्टॉक आइटम हैं, इसलिए MAX आवश्यक नहीं है, लेकिन आपको उस पर नज़र रखनी चाहिए। यदि आपके परिदृश्य में इतनी बड़ी स्ट्रिंग संभव है, तो आपको varchar(max)
का उपयोग करना होगा डिफ़ॉल्ट के बजाय (STRING_AGG()
रिटर्न nvarchar(max)
, लेकिन 8,000 बाइट्स तक छोटा हो जाता है जब तक कि इनपुट एक MAX प्रकार है)।
प्रारंभिक प्रश्न (नमूना आउटपुट दिखाने के लिए, और एकल निष्पादन के लिए अवधियों का निरीक्षण करने के लिए):
सेट स्टैटिस्टिक्स टाइम ऑन;गो सेलेक्ट ओ.ऑर्डरआईडी, स्टॉकआइटमआईडी =STRING_AGG(ol.StockItemID, ',') सेल्स से। इनर जॉइन सेल्स के रूप में ऑर्डर। .OrderID; GO सेलेक्ट o.OrderID, StockItemIDs =STUFF((Select ',' + CONVERT(varchar(11),ol.StockItemID) from Sales.OrderLines as ol WHERE ol.OrderID =o.OrderID for XML PATH('' ), TYPE).value(N'text()[1]',N'varchar(8000)'),1,1,'') Sales.O.O.OrderID द्वारा ग्रुप के रूप में ऑर्डर करें; सेट सांख्यिकी समय बंद करें; /* नमूना आउटपुट:ऑर्डरआईडी StockItemIDs ===================1 67 2 50,10 3 114 4 206,130,50 5 128,121,155 महत्वपूर्ण सेट सांख्यिकी समय मेट्रिक्स (एसक्यूएल सर्वर निष्पादन टाइम्स ):विंडोज़:STRING_AGG:CPU समय =217 ms, बीता हुआ समय =405 ms. एक्सएमएल पथ के लिए:सीपीयू समय =1954 एमएस, बीता हुआ समय =2097 एमएस। Linux:STRING_AGG:CPU समय =627 ms, बीता हुआ समय =472 ms. XML पथ के लिए:CPU समय =2188 ms, बीता हुआ समय =2223 ms.*/
मैंने पार्स को नजरअंदाज कर दिया और समय डेटा को पूरी तरह से संकलित कर दिया, क्योंकि वे हमेशा बिल्कुल शून्य थे या अप्रासंगिक होने के लिए पर्याप्त थे। प्रत्येक रन के निष्पादन समय में मामूली भिन्नताएं थीं, लेकिन ज्यादा नहीं - ऊपर दी गई टिप्पणियां रनटाइम में विशिष्ट डेल्टा को दर्शाती हैं (STRING_AGG
ऐसा लग रहा था कि वहां समानता का थोड़ा फायदा उठाया जा सकता है, लेकिन केवल लिनक्स पर, जबकि FOR XML PATH
किसी भी मंच पर नहीं)। दोनों मशीनों में एक ही सॉकेट, क्वाड-कोर सीपीयू आवंटित, 8 जीबी मेमोरी, आउट-ऑफ-द-बॉक्स कॉन्फ़िगरेशन और कोई अन्य गतिविधि नहीं थी।
तब मैं पैमाने पर परीक्षण करना चाहता था (बस एक ही सत्र 500 बार एक ही प्रश्न निष्पादित करता है)। मैं सभी आउटपुट को वापस नहीं करना चाहता था, जैसा कि उपरोक्त क्वेरी में है, 500 बार, क्योंकि इससे SSMS अभिभूत हो जाता - और उम्मीद है कि वैसे भी वास्तविक-विश्व क्वेरी परिदृश्यों का प्रतिनिधित्व नहीं करता है। इसलिए मैंने आउटपुट को वेरिएबल को सौंपा और प्रत्येक बैच के लिए कुल समय को मापा:
सेलेक्ट करें Sales.OrderLines के रूप में o.OrderID =ol.OrderID समूह द्वारा o.OrderID; GO 500 चुनें sysdatetime (); GO DECLARE @i int, @x varchar (8000); चुनें @i =o.OrderID, @x =STUFF((Select ',' + CONVERT(varchar(11),ol.StockItemID) from Sales.OrderLines as ol WHERE ol.OrderID =o.OrderID for XML PATH(''), TYPE).value(N'text( )[1]',N'varchar(8000)'),1,1,'') Sales.O.OrderID के अनुसार ग्रुप के रूप में ऑर्डर करें; 500 सेलेक्ट sysdatetime();मैंने उन परीक्षणों को तीन बार चलाया, और अंतर गहरा था - लगभग परिमाण का एक क्रम। यहां तीन परीक्षणों में औसत अवधि दी गई है:
वैरिएबल असाइनमेंट के 500 निष्पादन के लिए मिलीसेकंड में औसत अवधि
मैंने इस तरह से कई अन्य चीजों का भी परीक्षण किया, ज्यादातर यह सुनिश्चित करने के लिए कि मैं उन प्रकार के परीक्षणों को कवर कर रहा था जो ग्रेज़गोरज़ चल रहे थे (एलओबी भाग के बिना)।
- केवल आउटपुट की लंबाई का चयन करना
- आउटपुट की अधिकतम लंबाई प्राप्त करना (मनमानी पंक्ति का)
- सभी आउटपुट को एक नई तालिका में चुनना
केवल आउटपुट की लंबाई चुनना
यह कोड केवल प्रत्येक ऑर्डर के माध्यम से चलता है, सभी StockItemID मानों को जोड़ता है, और फिर केवल लंबाई देता है।
सेट स्टैटिस्टिक्स टाइम ऑन; GO SELECT LEN(STRING_AGG(ol.StockItemID, ',')) सेल्स से। ऑर्डर्स के रूप में ओ इनर जॉइन सेल्स। ऑर्डरलाइन्स ओल ऑन ओ। ऑर्डर आईडी =ओएल। ऑर्डर आईडी ग्रुप बाय ओ। ऑर्डर आईडी; GO SELECT LEN(STUFF((Select ',' + CONVERT(varchar(11),ol.StockItemID) from Sales.OrderLines as ol WHERE ol.OrderID =o.OrderID for XML PATH(''), TYPE).value( N'text()[1]',N'varchar(8000)'),1,1,'')) बिक्री से। O.OrderID द्वारा समूह के रूप में आदेश; सांख्यिकी समय बंद करें; /* विंडोज़:STRING_AGG:CPU समय =142 ms, बीता हुआ समय =351 ms. एक्सएमएल पथ के लिए:सीपीयू समय =1984 एमएस, बीता हुआ समय =2120 एमएस। Linux:STRING_AGG:CPU समय =310 ms, बीता हुआ समय =191 ms. XML पथ के लिए:CPU समय =2149 ms, बीता हुआ समय =2167 ms. */
बैच किए गए संस्करण के लिए, फिर से, मैंने एसएसएमएस को कई परिणाम वापस करने की कोशिश करने के बजाय परिवर्तनीय असाइनमेंट का उपयोग किया। परिवर्तनीय असाइनमेंट एक मनमानी पंक्ति पर समाप्त होगा, लेकिन इसके लिए अभी भी पूर्ण स्कैन की आवश्यकता है, क्योंकि पहले मनमानी पंक्ति का चयन नहीं किया जाता है।
सेलेक्ट sysdatetime (); गो डिक्लेयर @i int; सेलेक्ट @i =LEN (STRING_AGG (ol.StockItemID, ',')) सेल्स से। इनर जॉइन सेल्स के रूप में ऑर्डर। .OrderID ग्रुप o.OrderID द्वारा; GO 500 चुनें sysdatetime (); GO DECLARE @i int; SELECT @i =LEN(STUFF((Select ',' + CONVERT(varchar(11),ol.StockItemID) from Sales.OrderLines) जहाँ ol.OrderID =o.OrderID for XML PATH(''), TYPE).value(N'text()[1]',N'varchar(8000)'),1,1,'')) फ्रॉम सेल्स.ऑर्डर्स एएस ओ ग्रुप बाय ओ.ऑर्डरआईडी;गो 500 सेलेक्ट sysdatetime();
500 निष्पादन के प्रदर्शन मेट्रिक्स:
एक वेरिएबल को LEN() असाइन करने के 500 निष्पादन
फिर से, हम देखते हैं FOR XML PATH
विंडोज और लिनक्स दोनों पर बहुत धीमा है।
आउटपुट की अधिकतम लंबाई चुनना
पिछले परीक्षण पर थोड़ा सा बदलाव, यह सिर्फ अधिकतम . को पुनः प्राप्त करता है संयोजित आउटपुट की लंबाई:
सेट आँकड़ों का समय चालू करें; GO से MAX(s) चुनें (सेलेक्ट s =LEN(STRING_AGG(ol.StockItemID, ',')) सेल्स से। इनर जॉइन सेल्स के रूप में ऑर्डर। o.OrderID द्वारा ol.OrderID समूह) x के रूप में; GO से MAX (ओं) का चयन करें (चुनें s =LEN (STUFF ((चुनें ',' + CONVERT (varchar (11), ol.StockItemID)) बिक्री से। ऑर्डरलाइन एएस ओएल जहां ol.OrderID =o.OrderID for XML PATH(''), TYPE).value(N'text()[1]',N'varchar(8000)'), 1,1,'')) बिक्री से .O.O.OrderID द्वारा समूह के रूप में आदेश) x के रूप में; सांख्यिकी समय बंद करें; /* विंडोज़:STRING_AGG:CPU समय =188 ms, बीता हुआ समय =48 ms. XML पथ के लिए:CPU समय =1891 ms, बीता हुआ समय =907 ms. Linux:STRING_AGG:CPU समय =270 ms, बीता हुआ समय =83 ms. XML पथ के लिए:CPU समय =2725 ms, बीता हुआ समय =1205 ms.*/
और बड़े पैमाने पर, हम उस आउटपुट को फिर से एक वेरिएबल में असाइन करते हैं:
सेलेक्ट sysdatetime();GO DECLARE @i int; सेलेक्ट @i =MAX(s) FROM (सेलेक्ट s =LEN(STRING_AGG(ol.StockItemID, ',')) सेल्स से। आर्डर एएस ओ इनर जॉइन सेल्स। ऑर्डरलाइन्स एएस ओएल ऑन ओ। ऑर्डरआईडी =ओएल। ऑर्डरआईडी ग्रुप बाय ओ। ऑर्डरआईडी) एएस एक्स; GO 500 सेलेक्ट sysdatetime (); GO DECLARE @i int; सेलेक्ट @i =MAX(s) FROM (सेलेक्ट s =LEN (STUFF (STUFF)) (चयन ',' + कन्वर्ट (वर्कर (11), ol.StockItemID) बिक्री से। ऑर्डरलाइन्स ओएल जहां ओएल। ऑर्डर आईडी =ओ। एक्सएमएल पाथ (''), टाइप) के लिए ऑर्डर आईडी। मूल्य (एन' टेक्स्ट () [ 1]',N'varchar(8000)'), 1,1,'')) बिक्री से। O.OrderID के अनुसार समूह के रूप में आदेश) x के रूप में; 500 चुनें sysdatetime();
500 निष्पादन के लिए प्रदर्शन परिणाम, तीन रनों में औसत:
किसी वेरिएबल को MAX(LEN()) असाइन करने के 500 निष्पादन
आप इन परीक्षणों में एक पैटर्न देखना शुरू कर सकते हैं - FOR XML PATH
मेरी पिछली पोस्ट में सुझाए गए प्रदर्शन सुधारों के बावजूद, हमेशा एक कुत्ता है।
इसमें चुनें
मैं यह देखना चाहता था कि क्या संयोजन की विधि का लेखन . पर कोई प्रभाव पड़ा है? डेटा वापस डिस्क पर, जैसा कि कुछ अन्य परिदृश्यों में होता है:
NOCOUNT ON सेट करें;GOSET आँकड़ों का समय चालू करें; dbo.HoldingTank_AGG मौजूद होने पर ड्रॉप टेबल पर जाएं; o.OrderID, x =STRING_AGG(ol.StockItemID, ',') को बिक्री से dbo.HoldingTank_AGG में चुनें। इनर जॉइन सेल्स के रूप में ऑर्डर। तालिका यदि मौजूद है dbo.HoldingTank_XML; सेलेक्ट o.OrderID, x =STUFF((Select ',' + CONVERT(varchar(11),ol.StockItemID) from Sales.OrderLines as ol WHERE ol.OrderID =o.OrderID for XML PATH(''), TYPE) .value(N'text()[1]',N'varchar(8000)'),1,1,'') dbo में.HoldingTank_XML फ्रॉम सेल्स.ऑर्डर्स o ग्रुप बाय ओ.ऑर्डरआईडी;जाओ सेट स्टैटिस्टिक्स टाइम ऑफ; /* विंडोज़:STRING_AGG:CPU समय =218 ms, बीता हुआ समय =90 ms. एक्सएमएल पथ के लिए:सीपीयू समय =4202 एमएस, बीता हुआ समय =1520 एमएस। Linux:STRING_AGG:CPU समय =277 ms, बीता हुआ समय =108 ms. XML पथ के लिए:CPU समय =4308 ms, बीता हुआ समय =1583 ms.*/
इस मामले में हम देखते हैं कि शायद SELECT INTO
कुछ समानता का लाभ उठाने में सक्षम था, लेकिन फिर भी हम देखते हैं FOR XML PATH
संघर्ष, रनटाइम के साथ STRING_AGG
. से अधिक परिमाण का क्रम ।
बैच किए गए संस्करण ने SELECT sysdatetime();
के लिए SET STATISTICS कमांड को अभी-अभी स्वैप किया है और वही जोड़ा GO 500
पिछले परीक्षणों की तरह दो मुख्य बैचों के बाद। यहां बताया गया है कि यह कैसे हुआ (फिर से, मुझे बताएं कि क्या आपने इसे पहले सुना है):
चुनें INTO के 500 निष्पादन
आदेशित परीक्षण
मैंने आदेशित सिंटैक्स का उपयोग करके समान परीक्षण चलाए, उदा.:
... STRING_AGG(ol.StockItemID, ',') ग्रुप के अंदर (ऑर्डर द्वारा ol.StockItemID) ... ... जहां ol.OrderID =o.OrderID ऑर्डर OL.StockItemID द्वारा XML पाथ के लिए ('' ) ...
इसका किसी भी चीज़ पर बहुत कम प्रभाव पड़ा - चार टेस्ट रिग के एक ही सेट ने पूरे बोर्ड में लगभग समान मीट्रिक और पैटर्न दिखाए।
मुझे यह देखने के लिए उत्सुकता होगी कि क्या यह अलग है जब कॉन्टेनेटेड आउटपुट गैर-एलओबी में होता है या जहां कॉन्सटेनेशन को स्ट्रिंग्स को ऑर्डर करने की आवश्यकता होती है (सहायक इंडेक्स के साथ या बिना)।
निष्कर्ष
गैर-LOB स्ट्रिंग्स के लिए , मुझे यह स्पष्ट है कि STRING_AGG
FOR XML PATH
. पर एक निश्चित प्रदर्शन लाभ है , विंडोज और लिनक्स दोनों पर। ध्यान दें, varchar(max)
. की आवश्यकता से बचने के लिए या nvarchar(max)
, मैंने Grzegorz द्वारा चलाए गए परीक्षणों के समान कुछ भी उपयोग नहीं किया, जिसका अर्थ था कि एक कॉलम से सभी मानों को एक संपूर्ण तालिका में, एक ही स्ट्रिंग में जोड़ना। मेरी अगली पोस्ट में, मैं उपयोग के मामले पर एक नज़र डालूंगा जहां समेकित स्ट्रिंग का आउटपुट संभवतः 8,000 बाइट्स से अधिक हो सकता है, और इसलिए LOB प्रकार और रूपांतरणों का उपयोग करना होगा।