Database
 sql >> डेटाबेस >  >> RDS >> Database

बुरी आदतें :पंक्तियों को कठिन तरीके से गिनना

[सभी बुरी आदतों / सर्वोत्तम प्रथाओं की एक सूची देखें]

मेरी आवर्ती खराब आदतें और सर्वोत्तम अभ्यास प्रस्तुति में से एक स्लाइड का शीर्षक है "दुरुपयोग COUNT(*) ।" मैं इस दुर्व्यवहार को जंगली में बहुत कम देखता हूं, और इसके कई रूप होते हैं।

तालिका में कितनी पंक्तियाँ हैं?

मैं आमतौर पर इसे देखता हूं:

SELECT @count = COUNT(*) FROM dbo.tablename;

इस गणना को प्राप्त करने के लिए SQL सर्वर को संपूर्ण तालिका के विरुद्ध एक अवरुद्ध स्कैन चलाना पड़ता है। वह महंगा है। यह जानकारी कैटलॉग दृश्यों और DMV में संग्रहीत की जाती है, और आप इसे उन सभी I/O या अवरोधन के बिना प्राप्त कर सकते हैं:

SELECT @count = SUM(p.rows)
  FROM sys.partitions AS p
  INNER JOIN sys.tables AS t
  ON p.[object_id] = t.[object_id]
  INNER JOIN sys.schemas AS s
  ON t.[schema_id] = s.[schema_id]
  WHERE p.index_id IN (0,1) -- heap or clustered index
  AND t.name = N'tablename'
  AND s.name = N'dbo';

(आप वही जानकारी sys.dm_db_partition_stats . से प्राप्त कर सकते हैं , लेकिन उस स्थिति में p.rows . बदलें करने के लिए p.row_count (याय स्थिरता!)। वास्तव में, यह वही दृश्य है जो sp_spaceused गिनती प्राप्त करने के लिए उपयोग करता है - और जबकि उपरोक्त क्वेरी की तुलना में टाइप करना बहुत आसान है, मैं इसे सभी अतिरिक्त गणनाओं की वजह से गिनती प्राप्त करने के लिए इसका उपयोग करने के खिलाफ अनुशंसा करता हूं - जब तक कि आप वह जानकारी भी नहीं चाहते। यह भी ध्यान दें कि यह मेटाडेटा फ़ंक्शंस का उपयोग करता है जो आपके बाहरी अलगाव स्तर का पालन नहीं करते हैं, इसलिए जब आप इस प्रक्रिया को कॉल करते हैं तो आप अवरुद्ध होने की प्रतीक्षा कर सकते हैं।)

अब, यह सच है कि ये दृश्य 100% से लेकर माइक्रोसेकंड तक सटीक नहीं हैं। जब तक आप ढेर का उपयोग नहीं कर रहे हैं, तब तक sys.dm_db_index_physical_stats() से अधिक विश्वसनीय परिणाम प्राप्त किया जा सकता है। कॉलम record_count (फिर से एकरूपता!), हालांकि इस फ़ंक्शन का प्रदर्शन प्रभाव हो सकता है, फिर भी अवरुद्ध हो सकता है, और SELECT COUNT(*) से भी अधिक महंगा हो सकता है - इसे समान भौतिक संचालन करना होता है, लेकिन mode . के आधार पर अतिरिक्त जानकारी की गणना करनी होती है (जैसे विखंडन, जिसकी आपको इस मामले में परवाह नहीं है)। दस्तावेज़ीकरण में चेतावनी कहानी का हिस्सा बताती है, प्रासंगिक यदि आप उपलब्धता समूहों का उपयोग कर रहे हैं (और संभवतः इसी तरह डेटाबेस मिररिंग को प्रभावित करता है):

यदि आप सर्वर इंस्टेंस पर sys.dm_db_index_ Physical_stats को क्वेरी करते हैं जो एक ऑल्वेज़ऑन पठनीय द्वितीयक प्रतिकृति को होस्ट कर रहा है, तो आप एक REDO अवरोधन समस्या का सामना कर सकते हैं। ऐसा इसलिए है क्योंकि यह गतिशील प्रबंधन दृश्य निर्दिष्ट उपयोगकर्ता तालिका या दृश्य पर एक आईएस लॉक प्राप्त करता है जो उस उपयोगकर्ता तालिका या दृश्य पर एक्स लॉक के लिए आरईडीओ थ्रेड द्वारा अनुरोधों को अवरुद्ध कर सकता है।

प्रलेखन यह भी बताता है कि यह संख्या ढेर के लिए विश्वसनीय क्यों नहीं हो सकती है (और उन्हें पंक्तियों बनाम रिकॉर्ड असंगति के लिए एक अर्ध-पास भी देता है):

एक हीप के लिए, इस फ़ंक्शन से लौटाए गए रिकॉर्ड की संख्या, हीप के विरुद्ध SELECT COUNT(*) चलाकर लौटाई गई पंक्तियों की संख्या से मेल नहीं खा सकती है। ऐसा इसलिए है क्योंकि एक पंक्ति में कई रिकॉर्ड हो सकते हैं। उदाहरण के लिए, कुछ अद्यतन स्थितियों के तहत, अद्यतन कार्रवाई के परिणामस्वरूप एक एकल हीप पंक्ति में एक अग्रेषण रिकॉर्ड और एक अग्रेषित रिकॉर्ड हो सकता है। साथ ही, अधिकांश बड़ी LOB पंक्तियों को LOB_DATA संग्रहण में एकाधिक रिकॉर्ड में विभाजित किया जाता है।

तो मैं sys.partitions . की ओर झुकूंगा इसे अनुकूलित करने के तरीके के रूप में, कुछ मामूली सटीकता का त्याग करते हुए।

    "लेकिन मैं DMVs का उपयोग नहीं कर सकता; मेरी गिनती अति सटीक होनी चाहिए!"

    एक "सुपर सटीक" गिनती वास्तव में बहुत अर्थहीन है। आइए मान लें कि "सुपर सटीक" गणना के लिए आपका एकमात्र विकल्प पूरी तालिका को लॉक करना है और किसी को भी पंक्तियों को जोड़ने या हटाने से रोकना है (लेकिन साझा किए गए पढ़ने को रोके बिना), उदाहरण:

    SELECT @count = COUNT(*) FROM dbo.table_name WITH (TABLOCK); -- not TABLOCKX!

    तो, आपकी क्वेरी गुनगुना रही है, सभी डेटा स्कैन कर रही है, उस "परफेक्ट" गिनती की ओर काम कर रही है। इस बीच, लिखने के अनुरोध अवरुद्ध हो रहे हैं, और प्रतीक्षा कर रहे हैं। अचानक, जब आपकी सटीक गिनती वापस आ जाती है, तो टेबल पर आपके ताले जारी हो जाते हैं, और वे सभी लिखित अनुरोध जो कतारबद्ध और प्रतीक्षा कर रहे थे, आपकी तालिका के खिलाफ सभी प्रकार के आवेषण, अपडेट और डिलीट को फायर करना शुरू कर देते हैं। अब आपकी गिनती कितनी "सुपर सटीक" है? क्या यह "सटीक" गिनती प्राप्त करने लायक था जो पहले से ही अप्रचलित है? यदि सिस्टम व्यस्त नहीं है, तो यह कोई बड़ी समस्या नहीं है - लेकिन अगर सिस्टम व्यस्त नहीं है, तो मैं बहुत दृढ़ता से तर्क दूंगा कि DMVs बहुत सटीक होंगे।

    आप इस्तेमाल कर सकते थे NOLOCK इसके बजाय, लेकिन इसका मतलब यह है कि जब आप इसे पढ़ रहे हों तो लेखक डेटा को बदल सकते हैं, और अन्य समस्याएं भी पैदा कर सकते हैं (मैंने हाल ही में इस बारे में बात की थी)। बहुत सारे बॉलपार्क के लिए यह ठीक है, लेकिन अगर आपका लक्ष्य सटीकता है तो नहीं। बहुत सारे परिदृश्यों में DMV सही (या कम से कम बहुत करीब) होंगे, और बहुत कम में दूर होंगे (वास्तव में ऐसा कोई भी नहीं जिसके बारे में मैं सोच सकता हूं)।

    अंत में, आप प्रतिबद्ध स्नैपशॉट अलगाव पढ़ें का उपयोग कर सकते हैं। स्नैपशॉट अलगाव स्तरों के बारे में केंद्र लिटिल के पास एक शानदार पोस्ट है, लेकिन मैं अपने NOLOCK में उल्लिखित चेतावनियों की सूची दोहराऊंगा लेख:

    • एससी-एस लॉक अभी भी आरसीएसआई के तहत लेने की जरूरत है।
    • स्नैपशॉट अलगाव स्तर tempdb में पंक्ति संस्करण का उपयोग करते हैं, इसलिए आपको वास्तव में वहां प्रभाव का परीक्षण करने की आवश्यकता है।
    • RCSI कुशल आवंटन आदेश स्कैन का उपयोग नहीं कर सकता; आप इसके बजाय रेंज स्कैन देखेंगे।
    • पॉल व्हाइट (@SQL_Kiwi) के पास कुछ बेहतरीन पोस्ट हैं जिन्हें आपको इन अलगाव स्तरों के बारे में पढ़ना चाहिए:
      • प्रतिबद्ध स्नैपशॉट अलगाव पढ़ें
      • पढ़ें प्रतिबद्ध स्नैपशॉट अलगाव के अंतर्गत डेटा संशोधन
      • स्नैपशॉट अलगाव स्तर

    इसके अलावा, RCSI के साथ भी, "सटीक" गणना प्राप्त करने में समय लगता है (और tempdb में अतिरिक्त संसाधन)। ऑपरेशन समाप्त होने तक, क्या गिनती अभी भी सटीक है? केवल तभी जब इस दौरान किसी ने मेज को छुआ न हो। तो आरसीएसआई (पाठक लेखकों को ब्लॉक नहीं करते) के लाभों में से एक व्यर्थ है।

कितनी पंक्तियाँ WHERE क्लॉज़ से मेल खाती हैं?

यह थोड़ा अलग परिदृश्य है - आपको यह जानना होगा कि तालिका के एक निश्चित सबसेट के लिए कितनी पंक्तियाँ मौजूद हैं। आप इसके लिए DMV का उपयोग नहीं कर सकते, जब तक कि WHERE क्लॉज एक फ़िल्टर्ड इंडेक्स से मेल खाता है या पूरी तरह से एक सटीक विभाजन (या एकाधिक) को कवर करता है।

अगर आपका WHERE क्लॉज गतिशील है, जैसा कि ऊपर बताया गया है, आप RCSI का उपयोग कर सकते हैं।

अगर आपका WHERE खंड गतिशील नहीं है, आप RCSI का भी उपयोग कर सकते हैं, लेकिन आप इनमें से किसी एक विकल्प पर भी विचार कर सकते हैं:

  • फ़िल्टर की गई अनुक्रमणिका - उदाहरण के लिए यदि आपके पास is_active = 1 . जैसा कोई साधारण फ़िल्टर है या status < 5 , तो आप इस तरह से एक इंडेक्स बना सकते हैं:
    CREATE INDEX ix_f ON dbo.table_name(leading_pk_column) WHERE is_active = 1;

    अब, आप डीएमवी से बहुत सटीक गणना प्राप्त कर सकते हैं, क्योंकि इस सूचकांक का प्रतिनिधित्व करने वाली प्रविष्टियां होंगी (आपको केवल ढेर (0)/क्लस्टर इंडेक्स (1) पर निर्भर होने के बजाय index_id की पहचान करनी होगी)। हालांकि, आपको फ़िल्टर्ड इंडेक्स की कुछ कमजोरियों पर विचार करने की आवश्यकता है।

  • अनुक्रमित दृश्य - उदाहरण के लिए यदि आप अक्सर ग्राहक द्वारा ऑर्डर गिनते हैं, तो एक अनुक्रमित दृश्य मदद कर सकता है (हालांकि कृपया इसे एक सामान्य समर्थन के रूप में न लें कि "अनुक्रमित दृश्य सभी प्रश्नों को बेहतर बनाते हैं!"):
    CREATE VIEW dbo.view_name
    WITH SCHEMABINDING
    AS
      SELECT 
        customer_id, 
        customer_count = COUNT_BIG(*)
      FROM dbo.table_name
      GROUP BY customer_id;
    GO
     
    CREATE UNIQUE CLUSTERED INDEX ix_v ON dbo.view_name(customer_id);

    अब, दृश्य में मौजूद डेटा भौतिक हो जाएगा, और गणना तालिका डेटा के साथ सिंक्रनाइज़ होने की गारंटी है (कुछ अस्पष्ट बग हैं जहां यह सत्य नहीं है, जैसे कि यह MERGE के साथ है। , लेकिन आम तौर पर यह विश्वसनीय है)। तो अब आप दृश्य को क्वेरी करके प्रति ग्राहक (या ग्राहकों के एक समूह के लिए) अपनी गणना प्राप्त कर सकते हैं, बहुत कम क्वेरी लागत (1 या 2 रीड्स) पर:

    SELECT customer_count FROM dbo.view_name WHERE customer_id = <x>;

    यद्यपि कोई निःशुल्क लंच नहीं है . आपको एक अनुक्रमित दृश्य को बनाए रखने के ऊपरी हिस्से और आपके कार्यभार के लेखन भाग पर पड़ने वाले प्रभाव पर विचार करने की आवश्यकता है। यदि आप इस प्रकार की क्वेरी को बहुत बार नहीं चलाते हैं, तो यह समस्या के लायक होने की संभावना नहीं है।

क्या कम से कम एक पंक्ति WHERE क्लॉज से मेल खाती है?

यह भी थोड़ा अलग सवाल है। लेकिन मैं अक्सर इसे देखता हूं:

IF (SELECT COUNT(*) FROM dbo.table_name WHERE <some clause>) > 0 -- or = 0 for not exists

चूंकि आप स्पष्ट रूप से वास्तविक गणना की परवाह नहीं करते हैं, आप केवल तभी परवाह करते हैं जब कम से कम एक पंक्ति मौजूद हो, मुझे सच में लगता है कि आपको इसे निम्नलिखित में बदलना चाहिए:

IF EXISTS (SELECT 1 FROM dbo.table_name WHERE <some clause>)

तालिका के अंत तक पहुंचने से पहले इसमें कम से कम शॉर्ट-सर्किटिंग का मौका होता है, और लगभग हमेशा COUNT से बेहतर प्रदर्शन करेगा भिन्नता (हालांकि कुछ मामले ऐसे भी हैं जहां SQL सर्वर IF (SELECT COUNT...) > 0 कन्वर्ट करने के लिए पर्याप्त स्मार्ट है एक आसान IF EXISTS() ) सबसे खराब स्थिति में, जहां कोई पंक्ति नहीं मिलती है (या स्कैन में अंतिम पृष्ठ पर पहली पंक्ति पाई जाती है), प्रदर्शन वही होगा।

[सभी बुरी आदतों / सर्वोत्तम प्रथाओं की एक सूची देखें]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. विभिन्न त्रुटि प्रबंधन तकनीकों का प्रदर्शन प्रभाव

  2. पोकर, ब्लैकजैक, बेलोट और प्रेफरेंस का डेटाबेस से क्या लेना-देना है?

  3. सरल से स्लाइडिंग एकत्रीकरण तक SQL में डेटा एकत्र करने की कला

  4. चॉकलेटी पर होस्टिंग पैकेज

  5. SQL में एक वर्ग की गणना कैसे करें