PostgreSQL में एक तालिका की पंक्ति गणना की खोज करने का तेज़ तरीका


108

मुझे प्रतिशत की गणना करने के लिए तालिका में पंक्तियों की संख्या जानने की आवश्यकता है। यदि कुल संख्या कुछ पूर्वनिर्धारित स्थिरांक से अधिक है, तो मैं निरंतर मान का उपयोग करूंगा। अन्यथा, मैं वास्तविक संख्या में पंक्तियों का उपयोग करूंगा।

मैं उपयोग कर सकता हूं SELECT count(*) FROM table। लेकिन अगर मेरा निरंतर मूल्य 500,000 है और मेरी तालिका में 5,000,000,000 पंक्तियाँ हैं, तो सभी पंक्तियों को गिनने से बहुत समय बर्बाद होगा।

क्या मेरे निरंतर मूल्य को पार कर जाते ही गिनती रोकना संभव है?

मुझे पंक्तियों की सटीक संख्या की आवश्यकता है, जब तक कि यह दी गई सीमा से कम है। अन्यथा, यदि गणना सीमा से ऊपर है, तो मैं इसके बजाय सीमा मूल्य का उपयोग करता हूं और जितना संभव हो उतनी तेजी से उत्तर चाहता हूं।

कुछ इस तरह:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;

5
क्या आप अभी पहली n पंक्तियों को चुनने का प्रयास नहीं कर सकते हैं जहाँ n = स्थिर + 1 है ? यदि यह आपके स्थिरांक से अधिक रिटर्न देता है, तो आप जानते हैं कि आपको अपने निरंतर का उपयोग करना चाहिए, और यदि यह अच्छा नहीं है?
gddc

क्या आपके पास तालिका में एक पहचान या ऑटो वेतन वृद्धि क्षेत्र है
स्पार्की

1
@ स्पार्क: अनुक्रम समर्थित PKs को सन्निहित होने की गारंटी नहीं है, पंक्तियों को हटाया जा सकता है या गर्भपात के कारण होने वाले अंतराल हो सकते हैं।
म्यू

आपका अपडेट आपके मूल प्रश्न के विपरीत लगता है ... क्या आपको पंक्तियों की सही संख्या जानने की आवश्यकता है, या क्या आपको केवल सही संख्या जानने की आवश्यकता है यदि यह एक सीमा से नीचे है?
फ्लिमज़ी

1
@ RenatoDinhaniConceição: क्या आप उस सटीक समस्या की व्याख्या कर सकते हैं जिसे आप हल करने की कोशिश कर रहे हैं? मुझे लगता है कि नीचे दिए गए मेरे जवाब से आपको लगता है कि आपने शुरू में क्या कहा था। अपडेट यह बताता है कि आप गिनती (*) के साथ-साथ कई अन्य फ़ील्ड चाहते हैं। यह मदद करेगा यदि आप वास्तव में समझा सकते हैं कि आप क्या करने की कोशिश कर रहे हैं। धन्यवाद।
रितेश

जवाबों:


225

पोस्टग्रेएसक्यूएल में बड़ी तालिकाओं में पंक्तियों की गिनती धीमी गति से होती है। एक सटीक संख्या प्राप्त करने के लिए इसे MVCC की प्रकृति के कारण पंक्तियों की एक पूरी गणना करनी होती है । नाटकीय रूप से इसे गति देने का एक तरीका है यदि गिनती सही नहीं है जैसा कि आपके मामले में लगता है।

सटीक गणना प्राप्त करने के बजाय ( बड़ी तालिकाओं के साथ धीमी ):

SELECT count(*) AS exact_count FROM myschema.mytable;

आपको इस तरह का एक अत्यंत अनुमानित अनुमान है ( अत्यंत तेज़ ):

SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';

अनुमान कितना करीब है यह इस बात पर निर्भर करता है कि आप ANALYZEपर्याप्त भाग लेते हैं। यह आमतौर पर बहुत करीब है। PostgreSQL Wiki FAQ
देखें । या गिनती (*) प्रदर्शन के लिए समर्पित विकी पेज

और भी बेहतर

PostgreSQL विकी में लेख है था थोड़ा लापरवाह । इसने इस संभावना को नजरअंदाज कर दिया कि एक डेटाबेस में एक ही नाम के कई टेबल हो सकते हैं - अलग-अलग स्कीमा में। उस के लिए खाते में:

SELECT c.reltuples::bigint AS estimate
FROM   pg_class c
JOIN   pg_namespace n ON n.oid = c.relnamespace
WHERE  c.relname = 'mytable'
AND    n.nspname = 'myschema'

या अभी भी बेहतर है

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'myschema.mytable'::regclass;

तेज़, सरल, सुरक्षित, अधिक सुरुचिपूर्ण। ऑब्जेक्ट पहचानकर्ता प्रकार पर मैनुअल देखें ।

to_regclass('myschema.mytable')अमान्य तालिका नामों के अपवादों से बचने के लिए Postgres 9.4+ का उपयोग करें :


TABLESAMPLE SYSTEM (n) पोस्टग्रैज में 9.5+

SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);

जैसे @a_horse ने टिप्पणी की , SELECTकमांड के लिए नया जोड़ा क्लॉज उपयोगी हो सकता है यदि आंकड़े pg_classकिसी कारण से पर्याप्त नहीं हैं। उदाहरण के लिए:

  • नहीं autovacuumचल रहा है।
  • किसी बड़े के तुरंत बाद INSERTया DELETE
  • TEMPORARYतालिकाओं (जो द्वारा कवर नहीं हैं autovacuum)।

यह केवल ब्लॉक और यादृच्छिक पंक्तियों के चयन में एक यादृच्छिक n % ( 1उदाहरण में) को देखता है। एक बड़ा नमूना लागत को बढ़ाता है और त्रुटि को कम करता है, आपकी पिक। सटीकता अधिक कारकों पर निर्भर करती है:

  • पंक्ति आकार का वितरण। यदि किसी दिए गए ब्लॉक में सामान्य पंक्तियों की तुलना में व्यापक पकड़ होती है, तो गिनती सामान्य से कम होती है आदि।
  • मृत टुपल्स या FILLFACTORप्रति ब्लॉक एक व्यस्त स्थान। यदि असमान रूप से तालिका में वितरित किया जाता है, तो अनुमान बंद हो सकता है।
  • सामान्य दौर की त्रुटियां।

ज्यादातर मामलों में से अनुमान pg_classतेज और अधिक सटीक होगा।

वास्तविक प्रश्न का उत्तर

पहले, मुझे उस तालिका में पंक्तियों की संख्या जानने की आवश्यकता है, यदि कुल संख्या कुछ पूर्वनिर्धारित स्थिरांक से अधिक है,

और क्या यह ...

... इस समय यह संभव है कि गिनती मेरे निरंतर मूल्य को पार कर ले, यह गिनती को रोक देगा (और पंक्ति की संख्या अधिक होने की सूचना देने के लिए गिनती खत्म होने का इंतजार न करें)।

हाँ। आप इसके साथLIMIT एक उपश्रेणी का उपयोग कर सकते हैं :

SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;

पोस्टग्रैज वास्तव में दी गई सीमा से परे गिनती करना बंद कर देता है, आपको n पंक्तियों (उदाहरण में 500000) के लिए एक सटीक और वर्तमान गणना मिलती है , और अन्यथा n । हालांकि, अनुमान के अनुसार तेजी से नहीं ।pg_class


8
मैंने आखिरकार पोस्टग्रेज विकी पेज को बेहतर क्वेरी के साथ अपडेट किया।
एरविन ब्रान्डेसटेटर

5
9.5 का अनुमान लगाने के साथ tablesampleक्लॉज का उपयोग करना तेजी से संभव होना चाहिए : जैसेselect count(*) * 100 as cnt from mytable tablesample system (1);
a_horse_with_no_name

1
@ जेफेविडमैन: ये सभी अनुमान विभिन्न कारणों से वास्तविक पंक्ति गणना से अधिक हो सकते हैं। कम से कम, इस बीच डिलीट नहीं हुआ हो सकता है।
एरविन ब्रैंडस्टैटर

2
@ErwinBrandstetter को एहसास है कि यह प्रश्न पुराना है, लेकिन अगर आपने क्वेरी को सबक्वेरी में लपेट दिया है, तो क्या यह सीमा अभी भी कुशल होगी या पूरे सबक्वेरी को निष्पादित किया जाएगा तो बाहरी क्वेरी में सीमित होगा। SELECT count(*) FROM (Select * from (SELECT 1 FROM token) query) LIMIT 500000) limited_query;(मैं पूछता हूं क्योंकि मैं एक मनमाने ढंग से क्वेरी से एक गिनती प्राप्त करने की कोशिश कर रहा हूं जिसमें पहले से ही एक सीमा हो सकती है)
निकोलस एर्डनबर्गर

1
@ नाइकोलास एर्डेनबर्गर: यह सबक्वेरी पर निर्भर करता है। Postgres को वैसे भी सीमा से अधिक पंक्तियों पर विचार करने की आवश्यकता हो सकती है (जैसे कि ORDER BY somethingजब तक यह एक सूचकांक का उपयोग नहीं कर सकता है, या कुल कार्यों के साथ)। इसके अलावा, उपकुंजी से केवल सीमित संख्या में प्रक्रियाओं को संसाधित किया जाता है।
एरविन ब्रान्डस्टेट्टर

12

मैंने इसे एक बार पोस्टग्रास ऐप में चलाकर किया था:

EXPLAIN SELECT * FROM foo;

फिर रेगेक्स या इसी तरह के तर्क के साथ आउटपुट की जांच करना। एक साधारण चयन * के लिए, आउटपुट की पहली पंक्ति कुछ इस तरह दिखनी चाहिए:

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

आप rows=(\d+)मान का उपयोग उन पंक्तियों की संख्या के मोटे अनुमान के रूप में कर सकते हैं जिन्हें वापस किया जाएगा, फिर केवल वास्तविक करें SELECT COUNT(*)यदि अनुमान है, तो कहें, 1.5x से कम आपकी सीमा (या जो भी संख्या आप अपने आवेदन के लिए समझ में आता है)।

आपकी क्वेरी की जटिलता के आधार पर, यह संख्या कम और कम सटीक हो सकती है। वास्तव में, मेरे आवेदन में, जैसा कि हमने जुड़ने और जटिल परिस्थितियों में जोड़ा, यह इतना गलत हो गया कि यह पूरी तरह से बेकार था, यहां तक ​​कि यह जानने के लिए कि 100 पंक्तियों के भीतर हम कितनी पंक्तियों में वापस आ गए थे, इसलिए हमें उस रणनीति को छोड़ना पड़ा।

लेकिन अगर आपकी क्वेरी इतनी सरल है कि Pg त्रुटि के कुछ उचित मार्जिन के भीतर भविष्यवाणी कर सकता है कि यह कितनी पंक्तियाँ लौटाएगा, तो यह आपके लिए काम कर सकता है।


2

इस ब्लॉग से लिया गया संदर्भ।

आप पंक्ति गणना खोजने के लिए क्वेरी से नीचे का उपयोग कर सकते हैं।

Pg_class का उपयोग करना:

 SELECT reltuples::bigint AS EstimatedCount
    FROM   pg_class
    WHERE  oid = 'public.TableName'::regclass;

Pg_stat_user_tables का उपयोग करना:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;

बस जल्दी ध्यान दें कि आपको इस विधि को काम करने के लिए अपने टेबल को खाली करने की आवश्यकता है।
बजे विलियम अबमा

1

Oracle में, आप rownumदी गई पंक्तियों की संख्या को सीमित करने के लिए उपयोग कर सकते हैं । मैं अनुमान लगा रहा हूं कि अन्य SQL में भी समान निर्माण मौजूद है। इसलिए, आपके द्वारा दिए गए उदाहरण के लिए, आप 500001 पर लौटी पंक्तियों की संख्या सीमित कर सकते हैं और count(*)फिर आवेदन कर सकते हैं :

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)

1
सेलेक्ट काउंट (*) cnt FROM टेबल में हमेशा एक ही पंक्ति होगी। यह सुनिश्चित नहीं है कि लिमिट वहाँ कैसे कोई लाभ जोड़ने जा रहा है।
क्रिस बेडनार्स्की

@ क्रिसहेड्नार्स्की: मैंने ओरेकल डीबी पर अपने उत्तर के अलंकरण संस्करण का सत्यापन किया। यह बहुत अच्छा काम करता है और मैंने जो सोचा था, वह ओपी की समस्या थी ( count(*)उपद्रवी के साथ 0.05 सेकेंड, पंक्तिबद्ध उपयोग के बिना 1 एस)। हां, SELECT count(*) cnt FROM tableहमेशा 1 पंक्ति वापस करने जा रहा है, लेकिन लिमिट स्थिति के साथ, यह "500001" लौटेगा जब तालिका का आकार 500000 से अधिक होगा और <आकार> जब तालिका का आकार <= 500000 होगा।
रितेश

2
आपकी PostgreSQL क्वेरी पूरी बकवास है। सिंथेटिक और तार्किक रूप से गलत। कृपया इसे सुधारें या निकालें।
इरविन ब्रान्डेसटेटर

@ErwinBrandstetter: हटाया गया, नहीं लगा कि PostgreSQL इतना अलग था।
रितेश

@allrite: इसमें कोई शक नहीं कि आपकी Oracle क्वेरी ठीक काम करती है। सीमा हालांकि अलग तरह से काम करती है। एक बुनियादी स्तर पर, यह क्लाइंट के लिए लौटी पंक्तियों की संख्या को सीमित करता है, डेटाबेस इंजन द्वारा बुनी गई पंक्तियों की संख्या को नहीं।
क्रिस बेडनार्स्की

0

टेक्स्ट कॉलम कितना चौड़ा है?

एक ग्रुप बाय के साथ एक डेटा स्कैन (कम से कम एक इंडेक्स स्कैन) से बचने के लिए आप बहुत कुछ नहीं कर सकते।

मैं सुझाऊंगा:

  1. यदि संभव हो, तो पाठ डेटा के डुप्लीकेशन को हटाने के लिए स्कीमा को बदलना। इस तरह से गिनती 'कई' तालिका में एक संकीर्ण विदेशी कुंजी क्षेत्र पर होगी।

  2. वैकल्पिक रूप से, पाठ के एचएएसएच के साथ एक उत्पन्न कॉलम बनाना, फिर हैश कॉलम द्वारा ग्रुप। फिर, यह काम का बोझ कम करना है (एक संकीर्ण कॉलम इंडेक्स के माध्यम से स्कैन करें)

संपादित करें:

आपका मूल प्रश्न आपके संपादन से काफी मेल नहीं खाता था। मुझे यकीन नहीं है कि अगर आप जानते हैं कि COUNT, जब एक ग्रुप BY के साथ प्रयोग किया जाता है, तो प्रति समूह में आइटमों की गिनती लौटाएगा और संपूर्ण तालिका में आइटमों की गिनती नहीं।


0

आप नीचे दिए गए क्वेरी (* या किसी भी कॉलम नाम के बिना) के द्वारा प्राप्त कर सकते हैं।

select from table_name;

2
यह किसी भी तेजी से प्रतीत नहीं होता है count(*)
सनी

-3

SQL सर्वर (2005 या उससे ऊपर) के लिए एक त्वरित और विश्वसनीय तरीका है:

SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID('MyTableName')   
AND (index_id=0 or index_id=1);

MSDN में sysinos_db_partition_stats के बारे में विस्तार से बताया गया है

क्वेरी एक (संभवतः) विभाजन तालिका के सभी भागों से पंक्तियाँ जोड़ती है।

index_id = 0 एक अनियंत्रित तालिका (हीप) और index_id = 1 एक ऑर्डर की गई तालिका (क्लस्टर इंडेक्स) है

और भी तेज़ (लेकिन अविश्वसनीय) विधियाँ यहाँ विस्तृत हैं।

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