LIMIT / OFFSET के साथ एक क्वेरी चलाएँ और पंक्तियों की कुल संख्या भी प्राप्त करें


103

पृष्ठांकन प्रयोजनों के लिए, मुझे एक क्लॉस LIMITऔर OFFSETक्लॉज़ के साथ एक रन की आवश्यकता है । लेकिन मुझे उन पंक्तियों की संख्या की भी आवश्यकता है जो उस क्वेरी के बिना LIMITऔर OFFSETखंड से वापस आ जाएंगे ।

मैं दौड़ना चहता हूँ:

SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?

तथा:

SELECT COUNT(*) FROM table WHERE /* whatever */

एक ही समय पर। क्या ऐसा करने का एक तरीका है, विशेष रूप से ऐसा तरीका जो पोस्टग्रैजेस को इसे अनुकूलित करने देता है, ताकि यह दोनों व्यक्तिगत रूप से चलने से तेज हो?


जवाबों:


179

हाँ। एक साधारण विंडो फ़ंक्शन के साथ:

SELECT *, count(*) OVER() AS full_count
FROM   tbl
WHERE  /* whatever */
ORDER  BY col1
OFFSET ?
LIMIT  ?

ध्यान रखें कि कुल संख्या के बिना लागत काफी अधिक होगी, लेकिन आम तौर पर दो अलग-अलग प्रश्नों की तुलना में अभी भी सस्ता है। पोस्टग्रैज को वास्तव में सभी पंक्तियों को किसी भी तरह से गिनना पड़ता है , जो कुल पंक्तियों की संख्या के आधार पर लागत लगाता है। विवरण:

हालाँकि , जैसा कि दानी ने बताया , जब OFFSETआधार क्वेरी से लौटी पंक्तियों की संख्या कम से कम उतनी महान है, तो कोई पंक्तियाँ वापस नहीं आती हैं। तो हम भी नहीं मिलता है full_count

यदि यह स्वीकार्य नहीं है, तो एक पूर्ण वापसी के लिए एक संभावित समाधान एक सीटीई और एक के साथ होगा OUTER JOIN:

WITH cte AS (
   SELECT *
   FROM   tbl
   WHERE  /* whatever */
   )
SELECT *
FROM  (
   TABLE  cte
   ORDER  BY col1
   LIMIT  ?
   OFFSET ?
   ) sub
RIGHT  JOIN (SELECT count(*) FROM cte) c(full_count) ON true;

full_countयदि OFFSETबहुत बड़ा है, तो आपको संलग्न के साथ NULL मानों की एक पंक्ति मिलती है । और, यह पहली पंक्ति की तरह हर पंक्ति में संलग्न है।

यदि सभी NULL मानों के साथ एक पंक्ति एक संभावित वैध परिणाम है, तो आपको offset >= full_countखाली पंक्ति की उत्पत्ति को अस्वीकार करने के लिए जांचना होगा।

यह अभी भी केवल एक बार आधार क्वेरी निष्पादित करता है। लेकिन यह क्वेरी में अधिक ओवरहेड जोड़ता है और केवल तभी भुगतान करता है जब वह गणना के लिए आधार क्वेरी को दोहराने से कम हो।

यदि अंतिम प्रकार के आदेश का समर्थन करने वाले सूचकांक उपलब्ध हैं, तो यह ORDER BYसीटीई (अतिरेक) में शामिल करने के लिए भुगतान कर सकता है ।


3
दोनों लिमिट और शर्तों के अनुसार, हमारे पास वापस आने के लिए पंक्तियाँ हैं, लेकिन दिए गए ऑफसेट के साथ इसका कोई परिणाम नहीं होगा। उस स्थिति में, हम पंक्ति गणना कैसे कर पाएंगे?
दानी मैथ्यू

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

यदि आप इनपुट इनपुट पैरामीटर के माध्यम से क्वेरी में गतिशील रूप से सक्षम हो सकते हैं, तो क्या आप इस पर विस्तार से बता सकते हैं? मेरे पास एक समान आवश्यकता है लेकिन उपयोगकर्ता तय करता है कि वह इनलाइन काउंट करना चाहता है या नहीं।
ज्यूलगॉन

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

1
@JustinL .: अतिरिक्त उपरि केवल अपेक्षाकृत सस्ते आधार प्रश्नों के लिए महत्वपूर्ण होना चाहिए। इसके अलावा, Postgres 12 ने कई तरीकों से CTE के प्रदर्शन में सुधार किया है। (हालांकि यह सीटीई अभी भी MATERIALIZEDडिफ़ॉल्ट रूप से है, दो बार संदर्भित किया जा रहा है।)
इरविन ब्रान्डेसटेटर

0

संपादित करें: अनफ़िल्टर्ड तालिका को पुनः प्राप्त करते समय यह उत्तर मान्य है। यदि यह किसी की मदद कर सकता है तो मैं इसे करूँगा लेकिन यह प्रारंभिक प्रश्न का उत्तर नहीं दे सकता है।

यदि आपको एक सटीक मूल्य की आवश्यकता है, तो इरविन ब्रान्डसेट्टर का उत्तर सही है। हालांकि, बड़े तालिकाओं पर आपको अक्सर केवल एक बहुत अच्छा सन्निकटन की आवश्यकता होती है। Postgres आपको बस इतना देता है और यह बहुत तेज़ होगा क्योंकि इसे प्रत्येक पंक्ति का मूल्यांकन करने की आवश्यकता नहीं होगी:

SELECT *
FROM (
    SELECT *
    FROM tbl
    WHERE /* something */
    ORDER BY /* something */
    OFFSET ?
    LIMIT ?
    ) data
RIGHT JOIN (SELECT reltuples FROM pg_class WHERE relname = 'tbl') pg_count(total_count) ON true;

मैं वास्तव में निश्चित नहीं हूं कि क्या RIGHT JOINकोई मानक बनाने के लिए कोई फायदा है या एक मानक क्वेरी के रूप में है। यह कुछ परीक्षण के लायक होगा।

SELECT t.*, pgc.reltuples AS total_count
FROM tbl as t
RIGHT JOIN pg_class pgc ON pgc.relname = 'tbl'
WHERE /* something */
ORDER BY /* something */
OFFSET ?
LIMIT ?

2
त्वरित गणना अनुमान के बारे में: stackoverflow.com/a/7945274/939860 जैसा आपने कहा: पूरी तालिका को पुनः प्राप्त करते समय मान्य - जो WHEREआपके प्रश्नों में खंड द्वारा प्रतिवादित है । दूसरी क्वेरी यह तार्किक रूप से गलत है ( DB में हर तालिका के लिए एक पंक्ति को पुनः प्राप्त करता है ) - और तय होने पर अधिक महंगी।
एरविन ब्रान्डेसटेटर

0

जबकि इरविन Brandstetter के जवाब एक आकर्षण की तरह काम करता है, यह पंक्तियों की कुल संख्या रिटर्न प्रत्येक पंक्ति में निम्नलिखित की तरह:

col1 - col2 - col3 - total
--------------------------
aaaa - aaaa - aaaa - count
bbbb - bbbb - bbbb - count
cccc - cccc - cccc - count

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

total - rows
------------
count - [{col1: 'aaaa'},{col2: 'aaaa'},{col3: 'aaaa'}
         {col1: 'bbbb'},{col2: 'bbbb'},{col3: 'bbbb'}
         {col1: 'cccc'},{col2: 'cccc'},{col3: 'cccc'}]

SQL क्वेरी:

SELECT 
    (SELECT COUNT(*) FROM table) as count, 
    (SELECT json_agg(t.*) FROM (
        SELECT * FROM table
        WHERE /* whatever */
        ORDER BY col1
        OFFSET ?
        LIMIT ?
    ) AS t) AS rows 

-6

रिट्रेंड परिणाम की कुल पंक्तियों की संख्या प्राप्त करने के लिए बस के लिए दो बार एक ही क्वेरी को कॉल करने के लिए इसका बुरा अभ्यास। यह निष्पादन का समय लेगा और सर्वर संसाधन को बर्बाद करेगा।

बेहतर है, आप SQL_CALC_FOUND_ROWSक्वेरी में उपयोग कर सकते हैं जो MySQL को बताएगा कि पंक्ति क्वेरी की कुल संख्या को सीमा क्वेरी परिणामों के साथ लाया जाए।

उदाहरण के रूप में सेट करें:

SELECT SQL_CALC_FOUND_ROWS employeeName, phoneNumber FROM employee WHERE employeeName LIKE 'a%' LIMIT 10;

SELECT FOUND_ROWS();

उपरोक्त क्वेरी में, बस SQL_CALC_FOUND_ROWSबाकी आवश्यक क्वेरी में विकल्प जोड़ें और दूसरी पंक्ति निष्पादित करें अर्थात SELECT FOUND_ROWS()उस विवरण द्वारा दिए गए परिणाम सेट में पंक्तियों की संख्या लौटाएं।


1
समाधान के लिए पोस्टग्रेज की आवश्यकता होती है, न कि मायस्कल की।
मफिनमैन

@ मफिनमैन, आप mysql पर इसका उपयोग कर सकते हैं। MYSQL 4.0 के बाद से, यह क्वेरी में SQL_CALC_FOUND_ROWS विकल्प का उपयोग किया जा रहा है। लेकिन MYSQL 8.0 से यह सही नहीं है।
मोहम्मद राशिद

संबद्ध नहीं। इस सवाल का जवाब सालों पहले दिया गया था। यदि आप योगदान करना चाहते हैं, तो एक ही विषय के साथ एक नया प्रश्न पोस्ट करें लेकिन MySQL के लिए विशिष्ट।
मफिनमैन

हमेशा प्रासंगिक रहें
अली हुसैन

-15

नहीं।

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

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