2009-08-07 16 views
21

मैं प्रदर्शन की समस्याओं हो रही है जब LIMIT एक बड़े के साथ एक mysql SELECT ऑफसेट ing:मैं LIMIT क्लॉज में बड़े ऑफसेट के साथ एक MySQL क्वेरी कैसे बढ़ा सकता हूं?

SELECT * FROM table LIMIT m, n; 

हैं ऑफसेट m है, कहते हैं, बड़ा 1,000,000 से, आपरेशन बहुत धीमी है।

मुझे limit m, n का उपयोग करना होगा; मैं id > 1,000,000 limit n जैसे कुछ का उपयोग नहीं कर सकता।

बेहतर प्रदर्शन के लिए मैं इस कथन को कैसे अनुकूलित कर सकता हूं?

उत्तर

13

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

#create table to store sequences 
CREATE TABLE seq (
    seq_no int not null auto_increment, 
    id int not null, 
    primary key(seq_no), 
    unique(id) 
); 

#create the sequence 
TRUNCATE seq; 
INSERT INTO seq (id) SELECT id FROM mytable ORDER BY id; 

#now get 1000 rows from offset 1000000 
SELECT mytable.* 
FROM mytable 
INNER JOIN seq USING(id) 
WHERE seq.seq_no BETWEEN 1000000 AND 1000999; 
+3

यह दृष्टिकोण केवल चुनिंदा वक्तव्यों में काम करता है जिसमें स्थिति नहीं है। मेरी राय में यह एक अच्छा समाधान नहीं है। –

+3

इस सूचकांक तालिका को अद्यतन कैसे रखें? मेरे मामले में, मुझे डेटाटाइम कॉलम द्वारा ऑर्डर करना होगा और धीमी क्वेरी के परिणामस्वरूप बड़े ऑफसेट का उपयोग करना होगा। अगर मैं यह suport तालिका बना देता हूं, तो मुझे प्रत्येक बार एक नई तारीख फिर से डालने की आवश्यकता होगी, क्योंकि यह क्रम में नहीं आया है। मैं पहले से ही इस समाधान को देखता हूं, लेकिन अस्थायी तालिकाओं के साथ। –

9

वहाँ एक ब्लॉग पोस्ट कैसे आप सबसे अच्छा दिखाने के लिए संभव है, इस प्रकार के रूप में के रूप में कॉम्पैक्ट होना चाहिए पंक्तियों की चयन करना चाहिए पर इंटरनेट पर किसी है: सिर्फ आईडी; और पूर्ण परिणामों का उत्पादन करने के लिए आपको केवल चुनने वाली पंक्तियों के लिए इच्छित सभी डेटा प्राप्त करना चाहिए।

इस प्रकार, एसक्यूएल की तरह कुछ हो सकता है (अपरीक्षित, मैं यह वास्तव में किसी भी अच्छा हो जाएगा यकीन नहीं है):

select A.* from table A 
    inner join (select id from table order by whatever limit m, n) B 
    on A.id = B.id 
order by A.whatever 

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

अद्यतन: मुझे ब्लॉग पोस्ट मिला जिसके बारे में मैं बात कर रहा था: यह कोडिंग डरावनी पर जेफ एटवुड का "All Abstractions Are Failed Abstractions" था।

+0

मैंने आपके एसक्यूएल का सुझाव दिया है। लेकिन यह कोई सुधार नहीं करता है। –

+1

क्या होगा यदि आपके पास टेबल ए के आधार पर कोई क्लॉज है? यह काम नहीं करेगा, क्योंकि यह पहली सीमा है, फिर कहां लागू करें। यदि आप अपने सबक्वायरी के अंदर शामिल होने का उपयोग करते हैं, तो आप प्रदर्शन खो देंगे, है ना? –

+0

यह मेरे लिए काम करता है, 'से चुनें आईडी ...' क्वेरी को 'SELECT गुच्छा, फ़ील्ड से ...' की तुलना में लगभग दस लाख पंक्तियों के सेट पर लगभग 50 गुना तेजी से निष्पादित किया गया था। –

2

पॉल डिक्सन का जवाब वास्तव में समस्या का समाधान है, लेकिन आपको अनुक्रम तालिका को बनाए रखना होगा और यह सुनिश्चित करना होगा कि कोई पंक्ति अंतराल न हो।

यदि यह व्यवहार्य है, तो बेहतर समाधान यह सुनिश्चित करना होगा कि मूल तालिका में कोई पंक्ति अंतराल न हो और आईडी 1 से शुरू हो। फिर पेजिंग के लिए आईडी का उपयोग करके पंक्तियों को पकड़ें।

चयन करें * तालिका से जहां आईडी > = 1 और आईडी < = 1000;
चयन करें * तालिका से जहां आईडी > = 1001 और आईडी < = 2000;

और इतने पर ...

+0

चुनें * तालिका से आईडी> 1000 LIMIT 1000 –

+1

फिर, अन्य फ़िल्टर लागू होने पर यह काम नहीं करेगा। – devXen

2

मैं अगर अपनी मेज पहले से ही एक है एक अलग अनुक्रमणिका बनाने के लिए किसी भी आवश्यकता नहीं है नहीं लगता। यदि ऐसा है तो आप इस प्राथमिक कुंजी द्वारा आदेश कर सकते हैं और फिर से निकलने के लिए कुंजी के मान का उपयोग करें:

SELECT * FROM myBigTable WHERE id > :OFFSET ORDER BY id ASC; 

एक और अनुकूलन चुनें * लेकिन सिर्फ आईडी ताकि यह केवल सूचकांक पढ़ सकते हैं उपयोग करने के लिए नहीं होगा और फिर सभी डेटा का पता लगाने की आवश्यकता नहीं है (आईओ ओवरहेड को कम करें)।यदि आपको कुछ अन्य स्तंभों की आवश्यकता है तो शायद आप इसे इंडेक्स में जोड़ सकते हैं ताकि उन्हें प्राथमिक कुंजी के साथ पढ़ा जा सके (जो संभवतः स्मृति में आयोजित किया जाएगा और इसलिए डिस्क लुकअप की आवश्यकता नहीं है) - हालांकि यह उचित नहीं होगा सभी मामलों के लिए आपको एक खेलना होगा।

मैं अधिक विवरण के साथ एक लेख लिखा था:

http://www.4pmp.com/2010/02/scalable-mysql-avoid-offset-for-large-tables/

+0

क्या सिर्फ mysql या अधिकांश dbs इस अजीब तरीके से कार्य करता है? अब तक, सबसे अच्छा समाधान सबक्वायरी है (जब आपके पास ऑर्डर ऑर्डर नहीं होता है)। पहले सभी को क्वेरी करें और ऑर्डर करें, फिर ऑफसेट डालें। –

+0

केवल आईडी का उपयोग करने का विचार वास्तव में एक बहुत अच्छा समाधान हो सकता है, यह मुझे लगता है कि भंडारण इंजन पर निर्भर करता है! – twicejr

4

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

धीमी:

SELECT * FROM table ORDER BY id DESC LIMIT 10 OFFSET 50000 

तेजी:

SELECT id FROM table ORDER BY id DESC LIMIT 10 OFFSET 50000 

SELECT * FROM table WHERE id IN (1,2,3...10) 
0

मैंने हाल ही में इस समस्या में भाग लिया है। समस्या ठीक करने के लिए दो भागों था।

$subQuery = DB::raw("(SELECT id FROM titles WHERE id BETWEEN {$startId} AND {$endId} ORDER BY title) as t"); 

तो मैं इस्तेमाल कर सकते हैं कि के रूप में अपने प्रश्न का हिस्सा से:

'titles.id', 
          'title_eisbns_concat.eisbns_concat', 
          'titles.pub_symbol', 
          'titles.title', 
          'titles.subtitle', 
          'titles.contributor1', 
          'titles.publisher', 
          'titles.epub_date', 
          'titles.ebook_price', 
          'publisher_licenses.id as pub_license_id', 
          'license_types.shortname', 
          $coversQuery 
         ) 
         ->from($subQuery) 
         ->leftJoin('titles', 't.id', '=', 'titles.id') 
         ->leftJoin('organizations', 'organizations.symbol', '=', 'titles.pub_symbol') 
         ->leftJoin('title_eisbns_concat', 'titles.id', '=', 'title_eisbns_concat.title_id') 
         ->leftJoin('publisher_licenses', 'publisher_licenses.org_id', '=', 'organizations.id') 
         ->leftJoin('license_types', 'license_types.id', '=', 'publisher_licenses.license_type_id') 
सबसे पहले मैं अपने FROM खंड की है कि मेरी सीमित और केवल प्राथमिक कुंजी पर मेरे लिए offsetting किया में एक आंतरिक चयन का इस्तेमाल करना पड़ा

पहली बार मैंने यह प्रश्न बनाया था, मैंने MySQL में ऑफ़सेट और LIMIT का उपयोग किया था। यह ठीक काम करता है जब तक कि मुझे पिछले पृष्ठ 100 प्राप्त नहीं हुआ तब ऑफसेट असहनीय रूप से धीमा हो गया। मेरी आंतरिक पूछताछ में इसे बदलने के लिए इसे किसी भी पेज के लिए बढ़ा दिया गया। मुझे यकीन नहीं है कि MySQL ने ऑफ़सेट क्यों नहीं किया है, लेकिन लगता है कि इसे वापस फिर से रील करना है।

संबंधित मुद्दे