2012-05-25 13 views
16

के साथ टी-एसक्यूएल खराब प्रदर्शन SQL सर्वर में सामान्य तालिका अभिव्यक्तियों के बारे में मेरा एक प्रदर्शन प्रश्न है। हमारी डेवलपर टीम में जब हम अपने प्रश्नों का निर्माण करते हैं तो हम बहुत सी सीटीई का उपयोग करते हैं। मैं वर्तमान में एक प्रश्न पर काम कर रहा हूं जिसमें भयानक प्रदर्शन था। लेकिन मुझे पता चला कि अगर मैंने श्रृंखला के बीच में उस सीटीई तक सभी रिकॉर्ड्स को एक अस्थायी तालिका में डाला और फिर जारी रखा लेकिन उस टेम्पलेट टेबल से चयन करते हुए मैंने प्रदर्शन में काफी सुधार किया। अब मैं समझने में कुछ सहायता प्राप्त करना चाहता हूं कि इस प्रकार का परिवर्तन केवल इस विशिष्ट क्वेरी पर लागू होता है और क्यों आप नीचे देखे गए दो मामलों को प्रदर्शन में इतना अलग करते हैं। या क्या हम संभवतः हमारी टीम में सीटीई का उपयोग कर सकते हैं और क्या हम आम तौर पर इस मामले से सीखकर प्रदर्शन हासिल कर सकते हैं?सीटीई

यहाँ वास्तव में क्या हो रहा है मुझे करने के लिए समझाने की कोशिश करें ...

कोड पूरा हो गया है और आप और शायद 2005 भी एसक्यूएल सर्वर 2008 पर इसे चलाने के लिए सक्षम हो जाएगा। एक भाग पर टिप्पणी की गई है और मेरा विचार यह है कि आप दो मामलों को एक या दूसरे टिप्पणी करके स्विच कर सकते हैं। आप देख सकते हैं कि अपनी ब्लॉक टिप्पणियां कहां रखनी है, मैंने इन स्थानों को --block comment here और --end block comment here

यह धीमा प्रदर्शन करने वाला मामला है जो असम्बद्ध डिफ़ॉल्ट है। यहाँ आप कर रहे हैं:

--Declare tables to use in example. 
CREATE TABLE #Preparation 
(
    Date DATETIME NOT NULL 
    ,Hour INT NOT NULL 
    ,Sales NUMERIC(9,2) 
    ,Items INT 
); 

CREATE TABLE #Calendar 
(
    Date DATETIME NOT NULL 
) 

CREATE TABLE #OpenHours 
(
    Day INT NOT NULL, 
    OpenFrom TIME NOT NULL, 
    OpenTo TIME NOT NULL 
); 

--Fill tables with sample data. 
INSERT INTO #OpenHours (Day, OpenFrom, OpenTo) 
VALUES 
    (1, '10:00', '20:00'), 
    (2, '10:00', '20:00'), 
    (3, '10:00', '20:00'), 
    (4, '10:00', '20:00'), 
    (5, '10:00', '20:00'), 
    (6, '10:00', '20:00'), 
    (7, '10:00', '20:00') 

DECLARE @CounterDay INT = 0, @CounterHour INT = 0, @Sales NUMERIC(9, 2), @Items INT; 

WHILE @CounterDay < 365 
BEGIN 
    SET @CounterHour = 0; 
    WHILE @CounterHour < 5 
    BEGIN 
     SET @Items = CAST(RAND() * 100 AS INT); 
     SET @Sales = CAST(RAND() * 1000 AS NUMERIC(9, 2)); 
     IF @Items % 2 = 0 
     BEGIN 
      SET @Items = NULL; 
      SET @Sales = NULL; 
     END 

     INSERT INTO #Preparation (Date, Hour, Items, Sales) 
     VALUES (DATEADD(DAY, @CounterDay, '2011-01-01'), @CounterHour + 13, @Items, @Sales); 

     SET @CounterHour += 1; 
    END 
    INSERT INTO #Calendar (Date) VALUES (DATEADD(DAY, @CounterDay, '2011-01-01')); 
    SET @CounterDay += 1; 
END 

--Here the query starts. 
;WITH P AS (
    SELECT DATEADD(HOUR, Hour, Date) AS Hour 
     ,Sales 
     ,Items 
    FROM #Preparation 
), 
O AS (
     SELECT DISTINCT DATEADD(HOUR, SV.number, C.Date) AS Hour 
     FROM #OpenHours AS O 
      JOIN #Calendar AS C ON O.Day = DATEPART(WEEKDAY, C.Date) 
      JOIN master.dbo.spt_values AS SV ON SV.number BETWEEN DATEPART(HOUR, O.OpenFrom) AND DATEPART(HOUR, O.OpenTo) 
), 
S AS (
    SELECT O.Hour, P.Sales, P.Items 
    FROM O 
     LEFT JOIN P ON P.Hour = O.Hour 
) 

--block comment here case 1 (slow performing) 
--With this technique it takes about 34 seconds. 
,N AS (
     SELECT 
      A.Hour 
      ,A.Sales AS SalesOrg 
      ,CASE WHEN COALESCE(B.Sales, C.Sales, 1) < 0 
       THEN 0 ELSE COALESCE(B.Sales, C.Sales, 1) END AS Sales 
      ,A.Items AS ItemsOrg 
      ,COALESCE(B.Items, C.Items, 1) AS Items 
     FROM S AS A 
     OUTER APPLY (SELECT TOP 1 * 
        FROM S 
        WHERE Hour <= A.Hour 
         AND Sales IS NOT NULL 
         AND DATEDIFF(DAY, Hour, A.Hour) = 0      
        ORDER BY Hour DESC) B 
     OUTER APPLY (SELECT TOP 1 * 
        FROM S 
        WHERE Sales IS NOT NULL 
         AND DATEDIFF(DAY, Hour, A.Hour) = 0 
        ORDER BY Hour) C 
    ) 
--end block comment here case 1 (slow performing) 

/*--block comment here case 2 (fast performing) 
--With this technique it takes about 2 seconds. 
SELECT * INTO #tmpS FROM S; 

WITH 
N AS (
     SELECT 
      A.Hour 
      ,A.Sales AS SalesOrg 
      ,CASE WHEN COALESCE(B.Sales, C.Sales, 1) < 0 
       THEN 0 ELSE COALESCE(B.Sales, C.Sales, 1) END AS Sales 
      ,A.Items AS ItemsOrg 
      ,COALESCE(B.Items, C.Items, 1) AS Items 
     FROM #tmpS AS A 
     OUTER APPLY (SELECT TOP 1 * 
        FROM #tmpS 
        WHERE Hour <= A.Hour 
         AND Sales IS NOT NULL 
         AND DATEDIFF(DAY, Hour, A.Hour) = 0      
        ORDER BY Hour DESC) B 
     OUTER APPLY (SELECT TOP 1 * 
        FROM #tmpS 
        WHERE Sales IS NOT NULL 
         AND DATEDIFF(DAY, Hour, A.Hour) = 0 
        ORDER BY Hour) C 
    ) 
--end block comment here case 2 (fast performing)*/ 
SELECT * FROM N ORDER BY Hour 


IF OBJECT_ID('tempdb..#tmpS') IS NOT NULL DROP TABLE #tmpS; 

DROP TABLE #Preparation; 
DROP TABLE #Calendar; 
DROP TABLE #OpenHours; 

आप कोशिश करते हैं और समझते हैं कि मैं अंतिम चरण मैं इसे here के बारे में इतना सवाल है में क्या कर रहा हूँ करना चाहते हैं।

मेरे लिए केस 1 में लगभग 34 सेकंड लगते हैं और केस 2 में लगभग 2 सेकंड लगते हैं। अंतर यह है कि मैं 2 से मामले में एक टेम्प तालिका में एस को परिणाम संग्रहीत करता हूं, यदि 1 में मैं अपने अगले सीटीई में सीधे एस का उपयोग करता हूं।

+1

+1 रनने योग्य कोड के लिए। मैं उन्हें एसक्यूएल सेंट्री प्लान एक्सप्लोरर में निष्पादन योजना एक्सएमएल दोनों को चलाने और चिपकाने का सुझाव देता हूं। अंतर का कारण तब स्पष्ट होगा। यह योजना के एक हिस्से में '# तैयारी' 20,000 बार स्कैनिंग और उदाहरण के लिए किसी अन्य भाग में 10,000 बार स्कैनिंग समाप्त होता है। –

+0

धन्यवाद। मैंने एसक्यूएल सेंट्री प्लान एक्सप्लोरर स्थापित किया। मैं अभी भी एसक्यूएल सर्वर सीख रहा हूं और मैं निष्पादन योजनाओं को पढ़ने में सक्षम नहीं हूं, न तो सेंट्री प्लान में। लेकिन जवाब यह है कि निष्पादन योजनाओं की जांच किए बिना कुछ परिदृश्यों में सीटीई या अस्थायी तालिका के साथ सबसे अच्छा यह कहने के बारे में कुछ नहीं कह सकता है? आप 20,000 और 10,000 बार कहां देखते हैं? मुझे यह नहीं मिल रहा है। क्या कोड का जिक्र करते हुए एक भाग स्कैनिंग 20,000 बार क्यों स्कैन कर रहा है, इस बारे में कुछ कहना संभव है? – John

+0

क्या आपने यह देखा है? "एसक्यूएल 2005 सीटीई बनाम टीईएमपी तालिका प्रदर्शन जब अन्य तालिकाओं के जुड़ने में उपयोग किया जाता है" http://stackoverflow.com/questions/1531835/sql-2005-cte-vs-temp-table-performance-when-used-in-joins- अन्य-टेबल –

उत्तर

5

सीटीई सिर्फ वाक्यविन्यास शॉर्टकट है। उस सीटीई को शामिल होने में चलाया जाता है (और फिर से चलाया जाता है)। #temp के साथ इसे एक बार मूल्यांकन किया जाता है और फिर परिणाम में पुनः उपयोग किया जाता है।

प्रलेखन भ्रामक है।

MSDN_CTE

एक आम तालिका अभिव्यक्ति (CTE) एक अस्थायी परिणाम सेट के बारे में सोचा जा सकता है।

यह लेख बताता है यह बेहतर

PapaCTEarticle

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

तालिका मूल्य पैरामीटर

TVP

पर एक नजर डालें वे एक #temp जैसी संरचना लेकिन जितना नहीं भूमि के ऊपर है। वे केवल पढ़े जाते हैं लेकिन ऐसा लगता है कि आपको केवल पढ़ने की आवश्यकता है। #temp बनाना और छोड़ना अलग-अलग होगा लेकिन कम से कम सर्वर पर यह 0.1 सेकंड हिट होगा और टीवीपी के साथ अनिवार्य रूप से हिट नहीं होगा।

+0

टीवीपी इस के लिए एक अच्छी सिफारिश नहीं हैं। उनके साथ संभावित रूप से बहुत से मुद्दे हैं, जिनमें से कम से कम यह नहीं है कि वे लेनदेन के दायरे से बाहर मौजूद हैं और इसलिए वापस लुढ़का नहीं जा सकता है! – JNK

+0

@ जेएनके मेरी मदद करें। टीवीपी केवल पढ़ा जाता है तो मुझे क्यों परवाह नहीं है अगर इसे वापस नहीं किया जा सकता है। यह नमूना केवल कोई सम्मिलित, अद्यतन या हटा नहीं है। जोखिम कहां है? आपने मुझे एक से अधिक बार मदद की है और हमेशा सही रहा है। – Paparazzi

+1

मुझे आउटपुट या परीक्षण के अलावा किसी अन्य चीज़ के लिए टीवीपी का सामान्य अविश्वास है। वे अभी भी लॉग फ़ाइल में लिखते हैं और डिस्क आईओ ओवरहेड रखते हैं लेकिन वास्तव में परमाणु नहीं हैं और उन्हें अनुक्रमित नहीं किया जा सकता है, आंकड़े नहीं हैं, और निष्पादन योजनाएं हमेशा उनके लिए एक पंक्ति मानती हैं। – JNK

11

CTE अनिवार्य रूप से केवल एक डिस्पोजेबल दृश्य है।CTE कोड को तालिका अभिव्यक्ति के रूप में FROM खंड में डालने से कहीं अधिक तेज़ी से कोई प्रश्न नहीं उठाएगा।

आपके उदाहरण में, वास्तविक समस्या वह तारीख है जो मुझे विश्वास है।

आपके पहले (धीमे) मामले में प्रत्येक पंक्ति के लिए दिनांक कार्य चलाने की आवश्यकता होती है।

आपके दूसरे (तेज़) मामले के लिए वे एक बार चलाए जाते हैं और एक टेबल में संग्रहीत होते हैं।

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

सीटीई पर बहुत अधिक गहन पढ़ने के लिए, this question on DBA.SE देखें।

संबंधित मुद्दे