कुशल इंसर्ट क्लॉस्ड इंडेक्स के साथ एक तालिका बनाते हैं


28

मेरा एक SQL कथन है जो कॉलम TRACKING_NUMBER पर क्लस्टर किए गए अनुक्रमणिका वाली तालिका में पंक्तियाँ सम्मिलित करता है।

ईजी:

INSERT INTO TABL_NAME (TRACKING_NUMBER, COLB, COLC) 
SELECT TRACKING_NUMBER, COL_B, COL_C 
FROM STAGING_TABLE

मेरा प्रश्न है - क्या यह अनुक्रमणिका स्तंभ के लिए SELECT कथन में ORDER BY खंड का उपयोग करने में मदद करता है, या किसी भी लाभ को प्राप्त किया जाएगा जिसे ORDER BY खंड के लिए आवश्यक अतिरिक्त प्रकार से नकार दिया जाएगा?

जवाबों:


18

जैसा कि अन्य उत्तर पहले ही संकेत देते हैं कि SQL सर्वर स्पष्ट रूप से यह सुनिश्चित नहीं कर सकता है कि पंक्तियाँ क्रमबद्ध अनुक्रमित क्रम से पहले क्रमबद्ध हैं insert

यह इस बात पर निर्भर करता है कि योजना में क्लस्टर इंडेक्स ऑपरेटर के पास DMLRequestSortसंपत्ति सेट है या नहीं (जो बदले में डाली गई पंक्तियों की अनुमानित संख्या पर निर्भर करता है)।

यदि आपको लगता है कि SQL सर्वर इसे कम कर रहा है तो आप पृष्ठ विभाजन और ऑपरेशन से विखंडन को कम ORDER BYकरने के लिए SELECTक्वेरी में एक स्पष्ट जोड़ने के लिए जो भी कारण से लाभ उठा सकते हैं।INSERT

उदाहरण:

use tempdb;

GO

CREATE TABLE T(N INT PRIMARY KEY,Filler char(2000))

CREATE TABLE T2(N INT PRIMARY KEY,Filler char(2000))

GO

DECLARE @T TABLE (U UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),N int)

INSERT INTO @T(N)
SELECT number 
FROM master..spt_values
WHERE type = 'P' AND number BETWEEN 0 AND 499

/*Estimated row count wrong as inserting from table variable*/
INSERT INTO T(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2

/*Same operation using explicit sort*/    
INSERT INTO T2(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2
ORDER BY T1.N*1000 + T2.N


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T'), NULL, NULL, 'DETAILED')
;  


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T2'), NULL, NULL, 'DETAILED')
;  

दिखाता है कि Tबड़े पैमाने पर खंडित है

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
99.3116118225536             92535                92535                67.1668272794663               250000
99.5                         200                  200                  74.2868173956017               92535
0                            1                    1                    32.0978502594514               200

लेकिन T2विखंडन के लिए न्यूनतम है

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
0.376                        262                  62500                99.456387447492                250000
2.1551724137931              232                  232                  43.2438349394613               62500
0                            1                    1                    37.2374598468001               232

इसके विपरीत कभी-कभी आप SQL सर्वर को पंक्ति गणना को कम आंकने के लिए बाध्य कर सकते हैं जब आप जानते हैं कि डेटा पहले से ही सॉर्ट किया गया है और एक अनावश्यक प्रकार से बचने की इच्छा है। एक उल्लेखनीय उदाहरण तब है जब एक बड़ी संख्या में पंक्तियों को एक तालिका में एक newsequentialidसंकुल सूचकांक कुंजी के साथ डाला जाए । Denali SQL सर्वर से पहले SQL सर्वर के संस्करणों में एक अनावश्यक और संभावित रूप से महंगा सॉर्ट ऑपरेशन जोड़ता है । इससे बचा जा सकता है

DECLARE @var INT =2147483647

INSERT INTO Foo
SELECT TOP (@var) *
FROM Bar

SQL सर्वर तब अनुमान लगाएगा कि 100 पंक्तियों को आकार के बावजूद डाला जाएगा, Barजो उस सीमा से नीचे है जिस पर योजना में एक प्रकार जोड़ा गया है। हालांकि जैसा कि नीचे टिप्पणी में बताया गया है कि इसका मतलब यह है कि सम्मिलित रूप से न्यूनतम लॉगिंग का लाभ नहीं ले पाएंगे।



12

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

यदि आप स्पष्ट प्रकार के साथ और बिना प्रक्रिया के निष्पादन योजनाओं को पकड़ सकते हैं, तो उन्हें टिप्पणी के लिए अपने प्रश्न के साथ संलग्न करें।

संपादित करें: 2011-10-28 17:00

@ गोन्सालु का उत्तर यह दर्शाता है कि एक प्रकार का ऑपरेशन हमेशा होता है, यह ऐसा नहीं है। डेमो स्क्रिप्ट की आवश्यकता!

चूंकि स्क्रिप्ट काफी बड़ी हो रही थीं, इसलिए मैंने उन्हें जिस्ट में स्थानांतरित कर दिया । प्रयोग में आसानी के लिए, स्क्रिप्ट SQLCMD मोड का उपयोग करती हैं। टेस्ट 2K5SP3, दोहरे कोर, 8GB पर चलते हैं।

सम्मिलित परीक्षण तीन परिदृश्यों को कवर करते हैं:

  1. लक्ष्य के रूप में एक ही क्रम में स्टेजिंग डेटा क्लस्टरिंग इंडेक्स।
  2. रिवर्स क्रम में डेटा क्लस्टरिंग इंडेक्स का मंचन।
  3. कॉलिंग डेटा को कॉल 2 द्वारा क्लस्टर किया जाता है जिसमें एक यादृच्छिक INT होता है।

25 पंक्तियों को सम्मिलित करते हुए, पहला रन।

पहली दौड़, 25 पंक्तियाँ

सभी तीन निष्पादन योजनाएं समान हैं, योजना में कहीं भी कोई प्रकार नहीं होता है और क्लस्टर इंडेक्स स्कैन "आदेशित = गलत" होता है।

दूसरा भाग, 26 पंक्तियों को सम्मिलित करना।

2 रन, 26 पंक्तियाँ

इस बार योजनाएं अलग हैं।

  • पहले क्रमबद्ध अनुक्रम स्कैन को दिखाता है जैसा कि आदेश दिया गया है = गलत। स्रोत डेटा को उचित रूप से सॉर्ट किए जाने पर कोई प्रकार नहीं हुआ है।
  • दूसरे में गुच्छेदार सूचकांक स्कैन के रूप में आदेश दिया = सही, पिछड़ा। इसलिए हमारे पास एक सॉर्ट ऑपरेशन नहीं है लेकिन डेटा को सॉर्ट करने की आवश्यकता को ऑप्टिमाइज़र द्वारा मान्यता प्राप्त है और यह रिवर्स ऑर्डर में स्कैन करता है।
  • तीसरा एक सॉर्ट ऑपरेटर दिखाता है।

तो, एक टिपिंग बिंदु है जहां ऑप्टिमाइज़र आवश्यक होने के लिए एक प्रकार देता है। जैसा कि @MartinSmith दिखाता है, यह डाली जाने वाली अनुमानित पंक्तियों पर आधारित प्रतीत होता है। मेरे परीक्षण में रिग 25 के लिए एक प्रकार की आवश्यकता नहीं है, 26 करता है (2K5SP3, दोहरे कोर, 8 जीबी)

SQLCMD स्क्रिप्ट में वे चर शामिल होते हैं जो तालिका में पंक्तियों के आकार को बदलने की अनुमति देते हैं (पृष्ठ घनत्व में परिवर्तन) और अतिरिक्त आवेषण से पहले dbo में पंक्तियों की संख्या। यह योग्य है। मेरे परीक्षण से, न तो टिपिंग बिंदु पर कोई प्रभाव पड़ता है।

यदि कोई पाठक इतना झुका है, तो कृपया स्क्रिप्ट चलाएं और टिप्पणी के रूप में अपना टिपिंग बिंदु जोड़ें। यह सुनने के इच्छुक हैं कि क्या यह परीक्षण रिसाव और / या संस्करणों में भिन्न है।

संपादित करें: 2011-10-28 20:15

एक ही रिग पर दोहराए गए परीक्षण लेकिन 2K8R2 के साथ। इस बार टिपिंग पॉइंट 251 पंक्तियाँ हैं। फिर से, पृष्ठ घनत्व और मौजूदा पंक्ति गणना में कोई प्रभाव नहीं है।


8

ORDER BYमें खंड SELECTबयान अनावश्यक है।

यह निरर्थक है क्योंकि जो पंक्तियाँ डाली जाएंगी, अगर उन्हें क्रमबद्ध करने की आवश्यकता है , तो वैसे भी हल किया जाता है।

आइए हम एक टेस्ट केस बनाते हैं।

CREATE TABLE #Test (
    id INTEGER NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX CL_Test_ID ON #Test (id);

CREATE TABLE #Sequence (
    number INTEGER NOT NULL
);

INSERT INTO #Sequence
SELECT number FROM master..spt_values WHERE name IS NULL;

चलिए वास्तविक क्वेरी योजनाओं के पाठ प्रदर्शन को सक्षम करते हैं, इसलिए हम देख सकते हैं कि क्वेरी प्रोसेसर द्वारा क्या कार्य किए जाते हैं।

SET STATISTICS PROFILE ON;
GO

अब, INSERTएक ORDER BYखंड के बिना तालिका में 2k पंक्तियों को दें ।

INSERT INTO #Test
SELECT number
  FROM #Sequence

इस क्वेरी के लिए वास्तविक निष्पादन योजना निम्नलिखित है।

INSERT INTO #Test  SELECT number    FROM #Sequence
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

जैसा कि आप देख सकते हैं, वास्तविक INSERT होने से पहले एक सॉर्ट ऑपरेटर है।

अब, तालिका को साफ़ करें, और INSERT2k पंक्तियों को ORDER BYखंड के साथ तालिका में डालें।

TRUNCATE TABLE #Test;
GO

INSERT INTO #Test
SELECT number
  FROM #Sequence
 ORDER BY number

इस क्वेरी के लिए वास्तविक निष्पादन योजना निम्नलिखित है।

INSERT INTO #Test  SELECT number    FROM #Sequence   ORDER BY number
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

ध्यान दें कि यह वही निष्पादन योजना है जिसका उपयोग INSERTबिना ORDER BYखंड के कथन के लिए किया गया था ।

अब, Sortऑपरेशन की हमेशा आवश्यकता नहीं होती है, जैसा कि मार्क स्मिथ ने एक अन्य उत्तर में दिखाया है (यदि सम्मिलित की जाने वाली पंक्तियों की संख्या कम है), लेकिन ORDER BYखंड अभी भी उस मामले में निरर्थक है, क्योंकि एक स्पष्ट के साथ भी ORDER BY, कोई भी Sortऑपरेशन उत्पन्न नहीं होता है क्वेरी प्रोसेसर द्वारा।

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

2011-11-02 अपडेट किया गया है: जैसा कि मार्क स्मिथ ने दिखाया है , INSERTएक क्लस्टर इंडेक्स वाली तालिका में हमेशा छंटनी की आवश्यकता नहीं होती है - ORDER BYहालांकि उस मामले में भी यह खंड बेमानी है।

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