OFFSET / FETCH NEXT से कुल पंक्ति गणना हो रही है


92

इसलिए, मुझे एक फ़ंक्शन मिला है जो कई रिकॉर्ड देता है जिसे मैं अपनी वेबसाइट पर पेजिंग लागू करना चाहता हूं। यह मुझे सुझाव दिया गया था कि मैं इसे पूरा करने के लिए SQL Server 2012 में ऑफसेट / Fetch Next का उपयोग करता हूं। हमारी वेबसाइट पर, हमारे पास एक ऐसा क्षेत्र है जो रिकॉर्ड की कुल संख्या और उस समय आप किस पृष्ठ पर है।

इससे पहले, मैं पूरे रिकॉर्ड सेट को प्राप्त कर रहा था और उस प्रोग्राम पर पेजिंग का निर्माण करने में सक्षम था। लेकिन FETCH NEXT X ROWS ONLY के साथ SQL तरीके का उपयोग करने पर, मुझे केवल एक्स पंक्तियाँ दी गई हैं, इसलिए मुझे नहीं पता कि मेरा कुल रिकॉर्ड सेट क्या है और मेरे मिनट और अधिकतम पृष्ठों की गणना कैसे करें। जिस तरह से मैं ऐसा करने के बारे में बता सकता हूं वह दो बार फ़ंक्शन को कॉल कर रहा है और पहले पर पंक्तियों की एक गिनती कर रहा है, फिर FETCH NEXT के साथ दूसरा रन कर रहा है। क्या कोई बेहतर तरीका है जिससे मुझे दो बार क्वेरी नहीं चलानी पड़ेगी? मैं प्रदर्शन को गति देने की कोशिश कर रहा हूं, इसे धीमा नहीं कर रहा हूं।

जवाबों:


115

आप उपयोग कर सकते हैं COUNT(*) OVER()... यहां एक त्वरित उदाहरण का उपयोग किया गया है sys.all_objects:

DECLARE 
  @PageSize INT = 10, 
  @PageNum  INT = 1;

SELECT 
  name, object_id, 
  overall_count = COUNT(*) OVER()
FROM sys.all_objects
ORDER BY name
  OFFSET (@PageNum-1)*@PageSize ROWS
  FETCH NEXT @PageSize ROWS ONLY;

हालांकि, यह छोटे डेटा सेट के लिए आरक्षित होना चाहिए; बड़े सेटों पर, प्रदर्शन संक्षिप्त हो सकता है। बेहतर विकल्पों के लिए यह पॉल व्हाइट लेख देखें , जिसमें अनुक्रमणित दृश्य बनाए रखना शामिल है (जो केवल तभी काम करता है जब परिणाम अनफ़िल्टर्ड हो या आप WHEREअग्रिम में खंड जानते हों ) और ROW_NUMBER()ट्रिक्स का उपयोग करना ।


44
अधिक रिकॉर्ड वाली तालिका में, COUNT (*) OVER () में 1 मिनट और 3 सेकंड का समय लगा। जेम्स म्बर्ग द्वारा नीचे वर्णित दृष्टिकोण को समान डेटा-सेट को पुनः प्राप्त करने में 13 सेकंड का समय लगा। मुझे यकीन है कि काउंट ओवर दृष्टिकोण छोटे डेटा-सेट के लिए ठीक काम करता है, लेकिन जब आप वास्तव में बड़े होने लगते हैं तो यह काफी धीमा हो जाता है।
मैथ्यू_360

या आप बस COUNT (1) OVER () का उपयोग कर सकते हैं, जो तेज़ी से एक नरकंकाल है क्योंकि इसमें तालिका से वास्तविक डेटा नहीं पढ़ना है, जैसे कि गणना (*) करता है
ldx

1
@AaronBertrand सच में? इसका मतलब यह होना चाहिए कि आपके पास या तो एक सूचकांक है जिसमें सभी कॉलम शामिल हैं, या यह कि 2008R2 के बाद से इसमें बहुत सुधार हुआ है। उस संस्करण में, गणना (*) क्रमिक रूप से काम करती है, जिसका अर्थ है कि पहले * (जैसे: सभी कॉलम) का चयन किया जाता है, फिर गिना जाता है। यदि आपने एक गणना (1) की है, तो आप केवल एक स्थिरांक का चयन करते हैं, जो वास्तविक डेटा को पढ़ने की तुलना में बहुत तेज है।
ldx

5
@idx नहीं, यह नहीं है कि 2008 R2 में कैसे काम किया, या तो क्षमा करें। मैं 6.5 के बाद से SQL सर्वर का उपयोग कर रहा हूं और मुझे ऐसा समय याद नहीं है जब इंजन COUNT (*) या COUNT (1) दोनों के लिए सबसे संकीर्ण सूचकांक को स्कैन करने के लिए पर्याप्त स्मार्ट नहीं था। निश्चित रूप से 2000 के बाद से नहीं। लेकिन हे, मेरे पास 2008 R2 का एक उदाहरण है, क्या आप SQLfiddle पर एक रिप्रो सेट कर सकते हैं जो आपके द्वारा दावा किए गए इस अंतर को प्रदर्शित करता है? मैं इसे आजमाकर खुश हूं।
एरोन बर्ट्रेंड

2
एक sql सर्वर 2016 डेटाबेस पर, लगभग 25 लाख पंक्तियों के साथ एक तालिका पर खोज, लगभग 3000 परिणामों पर (कई जोड़ के साथ, तालिका-मूल्यवान फ़ंक्शन सहित) पेजिंग, यह मिलीसेकंड लिया - भयानक!
जेकरेक १17 ’

140

मुझे COUNT ( ) OVER () पद्धति का उपयोग करते हुए कुछ प्रदर्शन समस्याओं का सामना करना पड़ा (मुझे यकीन नहीं है कि यह सर्वर था क्योंकि इसे 10 रिकॉर्ड वापस करने के लिए 40 सेकंड लगे और फिर बाद में कोई समस्या नहीं थी।) इस तकनीक ने COUNT ( ) OVER () का उपयोग किए बिना सभी शर्तों के तहत काम किया और पूरा किया। वही चीज:

DECLARE 
    @PageSize INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, Name
    FROM Table
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)
SELECT *
FROM TempResult, TempCount
ORDER BY TempResult.Name
    OFFSET (@PageNum-1)*@PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY

32
यह वास्तव में बहुत बढ़िया होगा यदि एक चर के लिए COUNT (*) मान को बचाने की संभावना थी। मैं इसे अपने संग्रहित प्रक्रिया के एक बाहरी पैरामीटर के रूप में सेट कर सकूंगा। कोई विचार?
का

1
क्या गिनती को अलग तालिका में लाने का कोई तरीका है? ऐसा लगता है कि आप केवल पहले के चयन के बयान के लिए "TempResult" का उपयोग कर सकते हैं।
मैथ्यू_360

4
यह काम इतनी अच्छी तरह से क्यों करता है? पहले CTE में, सभी पंक्तियों का चयन किया जाता है, फिर भ्रूण को नीचे उतारा जाता है। मुझे लगता है कि पहले CTE में सभी पंक्ति का चयन चीजों को काफी धीमा कर देगा। किसी भी मामले में, इसके लिए धन्यवाद!
jbd

1
मेरे मामले में यह COUNT (1) OVER () की तुलना में धीमा हो गया है .. शायद क्योंकि चयन में एक फ़ंक्शन।
तिजु जॉन

1
यह छोटे डेटाबेस के लिए एकदम सही काम करता है जब पंक्तियाँ लाखों होती हैं तो इसमें बहुत अधिक समय लगता है।
किआ

1

जेम्स रॉबर्ट के जवाब पर आधारित :

यह एक वैकल्पिक उपयोग है Row_Number(), यदि आपके पास SQL ​​सर्वर 2012 नहीं है और आप OFFSET का उपयोग नहीं कर सकते हैं

DECLARE 
    @PageNumEnd INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, NAME
    FROM Tabla
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)

select * 
from
(
    SELECT
     ROW_NUMBER() OVER ( ORDER BY PolizaId DESC) AS 'NumeroRenglon', 
     MaxRows, 
     ID,
     Name
    FROM TempResult, TempCount

)resultados
WHERE   NumeroRenglon >= @PageNum
    AND NumeroRenglon <= @PageNumEnd
ORDER BY NumeroRenglon
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.