सबसे तेज कौन सा है? चयन करें SQL_CALC_FOUND_ROWS से 'तालिका', या चयनित COUNT (*)


176

जब आप SQL क्वेरी द्वारा दी जाने वाली पंक्तियों की संख्या को सीमित करते हैं, जिसका आमतौर पर पेजिंग में उपयोग किया जाता है, तो कुल रैंक के कुल निर्धारण के लिए दो तरीके हैं:

विधि 1

SQL_CALC_FOUND_ROWSमूल में विकल्प शामिल करें SELECT, और फिर दौड़कर कुल पंक्तियों को प्राप्त करें SELECT FOUND_ROWS():

SELECT SQL_CALC_FOUND_ROWS * FROM table WHERE id > 100 LIMIT 10;
SELECT FOUND_ROWS();  

विधि 2

क्वेरी को सामान्य रूप से चलाएँ, और फिर दौड़कर कुल पंक्तियों को प्राप्त करें SELECT COUNT(*)

SELECT * FROM table WHERE id > 100 LIMIT 10;
SELECT COUNT(*) FROM table WHERE id > 100;  

कौन सा तरीका सबसे अच्छा / सबसे तेज़ है?

जवाबों:


120

निर्भर करता है। इस विषय पर MySQL प्रदर्शन ब्लॉग पोस्ट देखें: http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

बस एक त्वरित सारांश: पीटर कहते हैं कि यह आपके अनुक्रमित और अन्य कारकों पर निर्भर करता है। पोस्ट के लिए कई टिप्पणियाँ कहने लगती हैं कि SQL_CALC_FOUND_ROWS लगभग हमेशा धीमी होती है - कभी-कभी 10x तक धीमी - दो प्रश्नों को चलाने की तुलना में।


27
मैं इसकी पुष्टि कर सकता हूं - मैंने सिर्फ 168,000 पंक्ति डेटाबेस पर 4 जॉइन के साथ एक क्वेरी को अपडेट किया। SQL_CALC_FOUND_ROWS20 सेकंड से अधिक के साथ सिर्फ पहली 100 पंक्तियों का चयन करना ; COUNT(*)5 सेकंड के लिए एक अलग क्वेरी का उपयोग करना (दोनों गणना + परिणाम प्रश्नों के लिए)।
सैम ड्यूफेल

9
बहुत दिलचस्प निष्कर्ष। चूंकि MySQL के दस्तावेज स्पष्ट रूप से बताते हैं कि SQL_CALC_FOUND_ROWSयह तेजी से होगा, मुझे आश्चर्य है कि किन स्थितियों में (यदि कोई है) तो यह वास्तव में तेज है!
20

12
पुराना विषय है, लेकिन उन लोगों के लिए जो अभी भी दिलचस्प हैं! 10 चेक से INNODB पर अपनी जांच पूरी कर ली। मैं बता सकता हूं कि यह 9.2 (1 क्वेरी) के खिलाफ 26 (2query) है। SQL_CALC_FOUND_ROWS tblA का चयन करें। * tblB.id AS 'b_id', tblB.city AS 'b_city', tblC.id AS के अनुसार। 'c_id', tblC.type के रूप में 'c_type', tblD.id के रूप में 'd_id', tblD.extype के रूप में 'd_extype', tblY.id के रूप में 'y_id', से tblY.ydt के रूप में y_ydt tblA, tblB, tblC, tblD, tblY जहां tblA.b = tblC.id और tblA.c = tblB.id और tblA.d = tblD.id और tblA.y = tblY.id
Al Po

4
मैंने अभी यह प्रयोग किया है और SQLC_CALC_FOUND_ROWS दो प्रश्नों की तुलना में बहुत तेज़ था। अब मेरी मुख्य तालिका 65k और कुछ सैकड़ों में से दो जोड़ है, लेकिन मुख्य क्वेरी SQLC_CALC_FOUND_ROWS के साथ या उसके बिना 0.18 सेकंड लेती है, लेकिन जब मैंने COUNT ( id) के साथ दूसरी क्वेरी चलाई तो यह केवल 0.40 सेकंड में चली गई ।
ट्रांसिलवार्ड

1
संभावित प्रदर्शन समस्याओं के अलावा, विचार करें कि FOUND_ROWS()MySQL 8.0.17 में पदावनत किया गया है। आप भी देखिए @ मधुर-भइया का जवाब
अरूकेउर

19

"सर्वश्रेष्ठ" दृष्टिकोण चुनते समय, गति से अधिक महत्वपूर्ण विचार आपके कोड की स्थिरता और शुद्धता हो सकती है। यदि ऐसा है, तो SQL_CALC_FOUND_ROWS बेहतर है क्योंकि आपको केवल एक क्वेरी बनाए रखने की आवश्यकता है। एकल क्वेरी का उपयोग पूरी तरह से मुख्य और गणना प्रश्नों के बीच एक सूक्ष्म अंतर की संभावना को रोकता है, जिससे एक गलत COUNT हो सकता है।


11
यह आपके सेट अप पर निर्भर करता है। यदि आप किसी प्रकार के ORM या क्वेरी बिल्डर का उपयोग कर रहे हैं, तो उसी का उपयोग करना बहुत आसान है जहां दोनों प्रश्नों के मानदंड, किसी गिनती के लिए चयनित फ़ील्ड स्वैप करें और सीमा छोड़ें। आपको कभी भी दो बार मापदंड नहीं लिखना चाहिए।
म्पेन

मुझे लगता है कि मैं दो साधारण रूप से मानक मानक का उपयोग करके कोड बनाए रखना चाहता हूं, SQL प्रश्नों को समझने में आसान है जो एक मालिकाना MySQL सुविधा का उपयोग करता है - जो नए MySQL संस्करणों में ध्यान देने योग्य नहीं है।
थोमसट्रेटर

15

MySQL ने SQL_CALC_FOUND_ROWSवर्जन 8.0.17 के साथ कार्यक्षमता को हटाना शुरू कर दिया है ।

इसलिए, हमेशा अपनी क्वेरी के साथ निष्पादित करने पर विचार करना पसंद किया जाता है LIMIT, और फिर दूसरी पंक्तियों के साथ COUNT(*)और बिना LIMITयह निर्धारित करने के लिए कि क्या अतिरिक्त पंक्तियाँ हैं।

से डॉक्स :

SQL_CALC_FOUND_ROWS क्वेरी संशोधक और FOUND_ROWS () फ़ंक्शन के साथ MySQL 8.0.17 के रूप में पदावनत किए गए हैं और भविष्य के MySQL संस्करण में हटा दिए जाएंगे।

COUNT (*) कुछ अनुकूलन के अधीन है। SQL_CALC_FOUND_ROWS के कारण कुछ अनुकूलन अक्षम हो जाते हैं।

इन प्रश्नों का उपयोग करें:

SELECT * FROM tbl_name WHERE id > 100 LIMIT 10;
SELECT COUNT(*) WHERE id > 100;

इसके अलावा, SQL_CALC_FOUND_ROWSआम तौर पर MySQL WL # 12615 में बताए गए मुद्दों को अधिक देखा गया है :

SQL_CALC_FOUND_ROWS में कई समस्याएं हैं। सबसे पहले, यह धीमा है। बार-बार, यह एलआईएमआईटी के साथ क्वेरी को चलाने के लिए सस्ता होगा और फिर एक ही क्वेरी के लिए एक अलग सेलेक्ट COUNT ( ) होगा, क्योंकि COUNT ( ) उन ऑप्टिमाइज़ेशन का उपयोग कर सकता है जो पूरे परिणाम सेट की खोज करते समय नहीं किए जा सकते हैं (जैसे फाइलर्स COUNT (*) के लिए छोड़ दिया जा सकता है, जबकि CALC_FOUND_ROWS के साथ, हमें सही परिणाम की गारंटी देने के लिए कुछ filesort ऑप्टिमाइज़ेशन को अक्षम करना होगा)

इससे भी महत्वपूर्ण बात, यह कई स्थितियों में बहुत अस्पष्ट शब्दार्थ है। विशेष रूप से, जब एक क्वेरी में कई क्वेरी ब्लॉक होते हैं (जैसे UNION के साथ), तो एक वैध क्वेरी का निर्माण करने के साथ-साथ एक ही समय में "होगा-होगा" पंक्तियों की संख्या की गणना करने का कोई तरीका नहीं है। जैसे-जैसे इटरेटर निष्पादक इस प्रकार के प्रश्नों की ओर बढ़ रहा है, वास्तव में उसी शब्दार्थ को बनाए रखने की कोशिश करना कठिन है। इसके अलावा, यदि क्वेरी में कई लिमिट्स हैं (जैसे व्युत्पन्न टेबल के लिए), तो यह जरूरी नहीं है कि उनमें से कौन सा SQL_CALC_FOUND_ROWS को संदर्भित करना चाहिए। इस प्रकार, इस तरह के निरर्थक प्रश्नों को आवश्यक रूप से पुनरावृत्त निष्पादक में अलग-अलग शब्दार्थ प्राप्त होंगे, जो पहले थे।

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


परमाणु संचालन के रूप में यह दो चयन कैसे करें? यदि कोई व्यक्ति SELECT COUNT (*) क्वेरी से पहले एक पंक्ति सम्मिलित करता है तो क्या होगा? धन्यवाद।
डोम

@ यदि आपके पास MySQL8 + है, तो आप विंडो फ़ंक्शन का उपयोग करके दोनों क्वेरी को एक ही क्वेरी में चला सकते हैं; लेकिन यह एक इष्टतम समाधान नहीं होगा क्योंकि अनुक्रमित का उपयोग ठीक से नहीं किया जाएगा। एक अन्य विकल्प के साथ इन दो प्रश्नों के चारों ओर है LOCK TABLES <tablename>और UNLOCK TABLES। तीसरा विकल्प और (सबसे अच्छा IMHO) पृष्ठ पर अंक लगाना है। कृपया पढ़ें: mariadb.com/kb/en/library/pagination-optimization
मधुर भैया

14

निम्नलिखित लेख के अनुसार: https://www.percona.com/blog/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

यदि आपके पास आपके क्लॉज़ पर एक इंडेक्स है (यदि आपके मामले में आईडी अनुक्रमित है), तो यह बेहतर है कि SQL_CALC_FOUND_ROWS का उपयोग न करें और इसके बजाय 2 प्रश्नों का उपयोग करें, लेकिन यदि आपके पास कोई ऐसा इंडेक्स नहीं है जो आपके क्लॉज़ में रखा हो (आपके मामले में आईडी) तो SQL_CALC_FOUND_ROWS का उपयोग करना अधिक कुशल है।


8

IMHO, 2 क्वेरी का कारण

SELECT * FROM count_test WHERE b = 666 ORDER BY c LIMIT 5;
SELECT count(*) FROM count_test WHERE b = 666;

SQL_CALC_FOUND_ROWS का उपयोग करने से अधिक तेज़ हैं

SELECT SQL_CALC_FOUND_ROWS * FROM count_test WHERE b = 555 ORDER BY c LIMIT 5;

एक विशेष मामले के रूप में देखा जाना चाहिए।

यह तथ्य ORDER / LIMIT के समतुल्य एक के चयन की तुलना में WHERE क्लॉज की चयनात्मकता पर निर्भर करता है।

जैसा कि Arvids ने टिप्पणी में बताया ( http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows-#comment-1174394 ), इस तथ्य का उपयोग करता है कि, या नहीं, का उपयोग करें या नहीं एक अस्थायी तालिका, यह जानने के लिए एक अच्छा आधार होना चाहिए कि SCFR तेज होगा या नहीं।

लेकिन, जैसा कि मैंने जोड़ा ( http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/##ment-8166482 ), परिणाम वास्तव में, वास्तव में मामले पर निर्भर करता है। किसी विशेष पेजिनेटर के लिए, आप इस निष्कर्ष पर पहुँच सकते हैं कि “3 प्रथम पृष्ठों के लिए, 2 प्रश्नों का उपयोग करें; निम्नलिखित पृष्ठों के लिए, एक SCFR का उपयोग करें ”!


6

कुछ अनावश्यक एसक्यूएल को हटाने और फिर COUNT(*)से तेज हो जाएगा SQL_CALC_FOUND_ROWS। उदाहरण:

SELECT Person.Id, Person.Name, Job.Description, Card.Number
FROM Person
JOIN Job ON Job.Id = Person.Job_Id
LEFT JOIN Card ON Card.Person_Id = Person.Id
WHERE Job.Name = 'WEB Developer'
ORDER BY Person.Name

फिर अनावश्यक भाग के बिना गिनें:

SELECT COUNT(*)
FROM Person
JOIN Job ON Job.Id = Person.Job_Id
WHERE Job.Name = 'WEB Developer'

3

आपके लिए बेंचमार्क करने के अन्य विकल्प हैं:

1.) एक विंडो फ़ंक्शन वास्तविक आकार को सीधे लौटाएगा (मारियाडीबी में परीक्षण किया गया):

SELECT 
  `mytable`.*,
  COUNT(*) OVER() AS `total_count`
FROM `mytable`
ORDER BY `mycol`
LIMIT 10, 20

2.) बॉक्स से बाहर सोचते हुए, अधिकांश समय उपयोगकर्ताओं को तालिका के सटीक आकार को जानने की आवश्यकता नहीं होती है , एक अनुमानित अक्सर काफी अच्छा होता है।

SELECT `TABLE_ROWS` AS `rows_approx`
FROM `INFORMATION_SCHEMA`.`TABLES`
WHERE `TABLE_SCHEMA` = DATABASE()
  AND `TABLE_TYPE` = "BASE TABLE"
  AND `TABLE_NAME` = ?
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.