UNIVIVOT (एक लूप जॉइन) के साथ बैच मोड का उपयोग कैसे करें?


12

मेरे पास निम्नलिखित फ़ॉर्म की एक क्वेरी है:

SELECT ...
FROM ColumnstoreTable cs
CROSS APPLY (
    SELECT *
    FROM (VALUES
        ('A', cs.DataA)
        , ('B', cs.DataB)
        , ('C', cs.DataC)
    ) x(Col0, Col1)
) someValues

यह प्रत्येक पंक्ति को कॉलमस्टोर-समर्थित उप-वर्ग ( ColumnstoreTable) से लेता है और उन पंक्तियों को गुणा करता है। यह अनिवार्य रूप से एक है UNPIVOT। असली क्वेरी इससे बड़ी है। क्वेरी का यह हिस्सा अन्य प्रोसेसिंग में फीड होता है।

यहाँ समस्या यह है कि इसे CROSS APPLYलूप जॉइन के रूप में लागू किया जाता है जो एक उचित विकल्प है। दुर्भाग्य से, लूप जॉइन बैच मोड का समर्थन नहीं करते हैं।

क्वेरी का यह हिस्सा बहुत महत्वपूर्ण है और मुझे संदेह है कि बैच मोड में इसे चलाने से प्रदर्शन के लिए बहुत फायदेमंद हो सकता है।

मैं इस क्वेरी को फिर से कैसे लिख सकता हूं ताकि मैं बैच मोड से बाहर न जाऊं?

मैंने इसके बजाय एक अस्थायी तालिका का उपयोग करने का प्रयास किया VALUES, लेकिन इस तथ्य को नहीं बदला कि हैश में शामिल होने के लिए कोई समानता नहीं है।

जवाबों:


12

मूल्यों के लिए एक # तालिका का उपयोग करने के लिए एक दृष्टिकोण हो सकता है और एक हैश जॉइन के लिए अनुमति देने के लिए एक डमी इक्विज़न कॉलम भी पेश किया जा सकता है। उदाहरण के लिए:

-- Create a #temp table with a dummy column to match the hash join
-- and the actual column you want
CREATE TABLE #values (dummy INT NOT NULL, Col0 CHAR(1) NOT NULL)
INSERT INTO #values (dummy, Col0)
VALUES (0, 'A'),
        (0, 'B'),
        (0, 'C')
GO

-- A similar query, but with a dummy equijoin condition to allow for a hash join
SELECT v.Col0, 
    CASE v.Col0 
        WHEN 'A' THEN cs.DataA 
        WHEN 'B' THEN cs.DataB 
        WHEN 'C' THEN cs.DataC
        END AS Col1
FROM ColumnstoreTable cs
JOIN #values v
    -- Join your dummy column to any numeric column on the columnstore,
    -- multiplying that column by 0 to ensure a match to all #values
    ON v.dummy = cs.DataA * 0

प्रदर्शन और क्वेरी योजना

यह दृष्टिकोण निम्नलिखित की तरह एक क्वेरी योजना देता है, और हैश मैच बैच मोड में किया जाता है:

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

अगर मैं की जगह SELECTएक साथ बयान SUMके CASEकंसोल के लिए उन सभी पंक्तियों स्ट्रीम और फिर कोई वास्तविक 100MM पंक्ति columnstore तालिका मैं चारों ओर झूठ बोल रही है पर क्वेरी चलाने के लिए होने से बचाने के क्रम में बयान, मैं देख काफी अच्छा प्रदर्शन अपेक्षित 300 मिमी उत्पन्न करने के लिए पंक्तियों:

CPU time = 33803 ms, elapsed time = 4363 ms.

और वास्तविक योजना में हैश ज्वाइन का अच्छा समानांतर होना दर्शाता है।

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

जब सभी पंक्तियों का मूल्य समान होता है, तो हैश के बारे में नोट समांतरिकरण में शामिल होते हैं

इस क्वेरी का प्रदर्शन पूरी तरह से हैश टेबल तक पहुंच में शामिल होने के जांच पक्ष पर प्रत्येक थ्रेड पर बहुत अधिक निर्भर करता है (हैश विभाजन संस्करण के विपरीत, जो सभी पंक्तियों को एक ही थ्रेड में मैप करेगा, यह देखते हुए कि केवल एक अलग मूल्य है के लिए dummyस्तंभ)।

सौभाग्य से, यह इस मामले में सच है (जैसा कि हम Parallelismजांच पक्ष पर एक ऑपरेटर की कमी से देख सकते हैं ) और मज़बूती से सच होना चाहिए क्योंकि बैच मोड एक एकल हैश तालिका बनाता है जो थ्रेड्स में साझा किया जाता है। इसलिए, प्रत्येक थ्रेड उनकी पंक्तियों को ले जा सकता है Columnstore Index Scanऔर उन्हें उस साझा हैश तालिका से मिला सकता है। SQL Server 2012 में, यह कार्यक्षमता बहुत कम अनुमानित थी क्योंकि एक स्पिल के कारण ऑपरेटर रो मोड में पुनः आरंभ होता है, दोनों बैच मोड का लाभ खो देते हैं और साथ Repartition Streamsही साथ जॉइन साइड के जांच पक्ष पर एक ऑपरेटर की आवश्यकता होती है जो इस मामले में थ्रेड स्क्यू का कारण होगा। । SQL सर्वर 2014 में बैच मोड में बने रहने के लिए स्पिलिंग की अनुमति देना एक बड़ा सुधार है।

मेरी जानकारी के लिए, पंक्ति मोड में यह साझा हैश तालिका क्षमता नहीं है। हालाँकि, कुछ मामलों में, आमतौर पर बिल्ड साइड पर 100 से कम पंक्तियों के अनुमान के साथ, SQL सर्वर प्रत्येक थ्रेड के लिए हैश तालिका की एक अलग प्रतिलिपि बनाएगा ( Distribute Streamsहैश ज्वाइन में अग्रणी द्वारा पहचाने जाने योग्य )। यह बहुत शक्तिशाली हो सकता है, लेकिन बैच मोड की तुलना में बहुत कम विश्वसनीय है क्योंकि यह आपके कार्डिनैलिटी अनुमानों पर निर्भर करता है और SQL सर्वर प्रत्येक थ्रेड के लिए हैश तालिका की पूरी प्रतिलिपि बनाने की लागत बनाम लाभों का मूल्यांकन करने की कोशिश कर रहा है।

यूनिअन ऑल: एक सरल विकल्प

पॉल व्हाइट ने कहा कि एक और, और संभावित रूप से सरल, विकल्प UNION ALLप्रत्येक मूल्य के लिए पंक्तियों को संयोजित करने के लिए उपयोग करना होगा । यह संभावना है कि आपकी सर्वश्रेष्ठ शर्त यह मानती है कि गतिशील रूप से इस SQL ​​को बनाना आपके लिए आसान है। उदाहरण के लिए:

SELECT 'A' AS Col0, c.DataA AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'B' AS Col0, c.DataB AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'C' AS Col0, c.DataC AS Col1
FROM ColumnstoreTable c

यह एक योजना भी तैयार करता है जो बैच मोड का उपयोग करने में सक्षम है और मूल उत्तर की तुलना में बेहतर प्रदर्शन प्रदान करता है। (हालांकि दोनों ही मामलों में प्रदर्शन इतना तेज़ है कि किसी भी टेबल पर डेटा का चयन या लेखन जल्दी से अड़चन बन जाता है।) UNION ALLदृष्टिकोण भी 0. से गुणा करने जैसे खेल खेलने से बचता है। कभी-कभी इसे सरल समझना सबसे अच्छा होता है!

CPU time = 8673 ms, elapsed time = 4270 ms.

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


यूनिअन ऑल ट्रिक जानना अच्छा है, लेकिन स्रोत क्वेरी केवल तालिका नहीं बल्कि स्वयं के द्वारा एक महंगा क्वेरी होने की स्थिति में विफल रहती है।
boot4life
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.