2010-01-08 11 views
5

मैं अभी भी MySQL के बारे में सीख रहा हूं। मैं एक बहुत ही बुनियादी त्रुटि कर रहा हूं, और मैं यहां दबाए जाने के लिए तैयार हूं ...मेरी mysql क्वेरी को धीमा कर एक गिनती() गणना कर रहा है?

यह क्वेरी क्या करने का प्रयास कर रही है, पुस्तक की संख्या की गणना के आधार पर हमारी वेबसाइट के शीर्ष सदस्यों का चयन करें और नुस्खा समीक्षा उन्होंने बनाई है।

मैं SQL क्वेरी में कुल की गणना कर रहा हूं। क्वेरी धीमी है (9 सेकंड) और निश्चित रूप से स्केल नहीं करेगा क्योंकि हमारे पास केवल 400 सदस्य हैं और कुछ हज़ार समीक्षाएं हैं और यह काफी तेजी से बढ़ रही है।

मुझे लगता है कि यह एक पूर्ण टेबल स्कैन कर रहा है, और यह गणना इसे धीमा कर रही है, लेकिन मुझे ऐसा करने का एक वैकल्पिक तरीका पता नहीं है और कुछ ज्ञान पसंद आएगा।

यहाँ SQL विवरण है:

+----+-------------+----------------+-------+-------------------+-------------------+---------+---------------------+------+---------------------------------+ 
| id | select_type | table   | type | possible_keys  | key    | key_len | ref     | rows | Extra       | 
+----+-------------+----------------+-------+-------------------+-------------------+---------+---------------------+------+---------------------------------+ 
| 1 | SIMPLE  | users   | index | NULL    | PRIMARY   | 4  | NULL    | 414 | Using temporary; Using filesort | 
| 1 | SIMPLE  | recipe_reviews | ref | recipe_reviews_fk | recipe_reviews_fk | 5  | users.ID   | 12 |         | 
| 1 | SIMPLE  | book_reviews | ref | user_id   | user_id   | 5  | users.ID   | 4 |         | 
| 1 | SIMPLE  | bookshelf  | ref | recipe_reviews_fk | recipe_reviews_fk | 5  | users.ID   | 13 |         | 
+----+-------------+----------------+-------+-------------------+-------------------+---------+---------------------+------+---------------------------------+ 

अद्यतन & हल:

SELECT users.*, COUNT(DISTINCT bookshelf.ID) AS titles, COUNT(DISTINCT book_reviews.ID) as bookreviews, COUNT(DISTINCT recipe_reviews.ID) AS numreviews, COUNT(DISTINCT book_reviews.ID) + COUNT(DISTINCT recipe_reviews.ID) as reviewtotal 
FROM users 
LEFT OUTER JOIN recipe_reviews ON recipe_reviews.user_id = users.ID 
LEFT OUTER JOIN book_reviews ON book_reviews.user_id = users.ID 
LEFT OUTER JOIN bookshelf ON users.ID = bookshelf.user_id 
GROUP BY users.ID 
ORDER BY reviewtotal DESC 
LIMIT 8 

यहाँ विवरण है

मैंने महसूस किया, और इस बात की पुष्टि @recursive, तो वह पूछताछ है समस्या की जड़। मुझे इस से कार्टेशियन उत्पाद मिल रहा है। मैं सबक्वेरी की एक श्रृंखला के रूप में यह दुबारा लिखा और अंतिम काम कर कोड यहाँ है:

SELECT *, bookreviews + recipereviews AS totalreviews 
FROM (SELECT users.*, 
      (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
      (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
      (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
    FROM users) q 

यह मैं मिलीसेकेंड में एक परिणाम देता है। जॉइन के साथ ऐसा करने के तरीके भी हैं। यदि आप इसका पालन करना चाहते हैं तो How to add together the results of several subqueries? देखें।

+0

मैंने रिकर्सिव के उत्तर को सही के रूप में चिह्नित किया है, हालांकि उनका प्रारंभिक उत्तर समाधान नहीं है, उन्होंने इसे निम्नलिखित टिप्पणियों में खींचा। – mandel

उत्तर

2

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

+0

मैंने कोशिश की और प्रत्येक गिनती क्षेत्रों के लिए हजारों की गिनती के साथ समाप्त हो गया। – mandel

+0

ऐसा लगता है कि आपके पास डुप्लिकेट रिकॉर्ड आपके डेटाबेस हो सकते हैं। क्या आपने यह देखने के लिए अपनी टेबल की जांच की है कि वे समझ में हैं या नहीं? – recursive

+0

मैं सुनिश्चित करने के लिए तालिकाओं की समीक्षा करूंगा - शायद मुझे इनमें से कुछ पर सीधे कुंजी की बजाय फ़ील्ड के संयोजन के रूप में प्राथमिक कुंजी बनाना होगा। बुकशेल्फ़, उदाहरण के लिए, आईडी, user_id, cookbook_id है। User_id और cookbook_id का कॉम्बो अद्वितीय होना चाहिए ... – mandel

3

कि जैसी सुविधाओं के लिए, यह हमेशा कैशिंग के कुछ प्रकार के साथ काम करने ...

यह पहले से ही एक रात के आधार पर सभी उपयोगकर्ताओं के लिए रकम बना सकते हैं और उपयोगकर्ता के साथ उन लोगों रकम स्टोर करने के लिए मदद कर सकता है उपयोगी है। यह बहुत मदद करेगा और आपकी खोज तेज करेगा।

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

+0

मेरा सुझाव है कि आप राशि के अतिरिक्त उस बैच-गणना राशि के लिए "के रूप में" तिथि भी जोड़ दें। –

0

मैं अक्सर लगता है कि एक बड़ी मेज से एक छोटे अस्थायी तालिका बनाने होगा उल्लेखनीय गति लाभ।

तो बुनियादी प्रक्रिया:

  1. दुकान क्वेरी (मिलती है) के साथ अस्थायी तालिका में अस्थायी तालिका पर
  2. रन गिनती/सारांश प्रश्नों
2

user_id पर सभी तालिकाएं इंडेक्स करें। अगर यह अभी तक नहीं किया गया है तो यह परिमाण के आदेशों से आसानी से इस क्वेरी को गति दे सकता है।

+0

हां, उपयोगकर्ता_आईडी फ़ील्ड में से प्रत्येक पर पहले ही इंडेक्स हैं। – mandel

0

उपयोगकर्ता तालिका में कॉलम के रूप में प्रति उपयोगकर्ता समीक्षाओं की संख्या क्यों न केवल स्टोर करें?उपयोगकर्ता द्वारा की जाने वाली प्रत्येक नई समीक्षा को उनके उपयोगकर्ता रिकॉर्ड समीक्षा गिनती के मूल्य की वृद्धि की भी आवश्यकता होनी चाहिए।

उदाहरण के लिए:

user_id user_name number_of_reviews 
1  bob  5 
2  jane  10 

बॉब में एक नई समीक्षा डालता है, और आप उसका नंबर 6:

review_id user_id review_text 
16  1  "Great!" 

user_id user_name number_of_reviews 
1  bob  6 
2  jane  10 

अब आप बस इस तरह शीर्ष 5 समीक्षक प्राप्त कर सकते हैं:

SELECT * FROM users ORDER BY number_of_reviews DESC LIMIT 5 
+0

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

+0

मैं आपके डिजाइन में एक समस्याग्रस्त परिदृश्य के बारे में नहीं सोच सकता कि यह (number_of_reviews) जोखिम भरा होगा। यदि यह वास्तविक भौतिक सूची या धन राशि का प्रतिनिधित्व करता है, तो मैं थोड़ी अधिक सावधानी बरतता हूं। लेकिन अन्यथा यह पर्याप्त होना चाहिए। अपने आप को कठिन मत बनाओ! –

+0

इसके अलावा, अगर आपको कभी संदेह था कि गिनती बंद थी, तो आप उपरोक्त क्या कर रहे थे (गिनती * के साथ) में कोई अंतर है या नहीं, यह देखने के लिए कि आप "ऑफ़लाइन" डेटाबेस प्रतिलिपि पर प्रति उपयोगकर्ता समीक्षाओं की संख्या को फिर से समझ सकते हैं एक शामिल)। –

1

आप इस क्वेरी के साथ बहुत सी चीजें पूरी करने की कोशिश कर रहे हैं। मुझे आपके डीबी/क्वेरी डिज़ाइन के साथ समस्याएं दिखाई देती हैं। Book_shelf में आपके पास user_id क्यों है?

SELECT users.*, COUNT(book_reviews.ID) as bookreviews, COUNT(recipe_reviews.ID) AS recipereviews, bookreviews + recipereviews as reviewtotal 
    FROM users 
    LEFT OUTER JOIN recipe_reviews ON recipe_reviews.user_id = users.ID 
    LEFT OUTER JOIN book_reviews ON book_reviews.user_id = users.ID 
    GROUP BY users.ID 
    ORDER BY reviewtotal DESC 

आप कर सकते हैं भी कुल दोनों उपयोगकर्ताओं और पुस्तकों पर, तो recipe_reviews सहित नहीं करता है: कैसे निम्न तालिका संरचना के बारे में

CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT , 
name VARCHAR(20) NOT NULL , 
PRIMARY KEY (`id`) 
) 

CREATE TABLE recipe_reviews (
id INT NOT NULL AUTO_INCREMENT , 
review VARCHAR(20), 
user_id INT, 
PRIMARY KEY (id), 
FOREIGN KEY (user_id) references users(id) 
) 

CREATE TABLE bookshelf (
id INT NOT NULL AUTO_INCREMENT , 
name VARCHAR(20) NOT NULL , 
PRIMARY KEY (id) 
) 

CREATE TABLE book_reviews (
id INT NOT NULL AUTO_INCREMENT , 
review VARCHAR(20), 
user_id INT, 
bookshelf_id INT, 
PRIMARY KEY (id), 
FOREIGN KEY (user_id) references users(id), 
FOREIGN KEY (bookshelf_id) references bookshelf(id) 
) 

आप उन पर इकट्ठा करना चाहते हैं, तो आप अपना प्रश्न है सही बात।

पीएस: आपको डिस्टिंट की आवश्यकता नहीं है क्योंकि आपके पास चाबियाँ हैं।

+0

आपके विचारों के लिए धन्यवाद। हालांकि, बुकशेल्फ़ में उपयोगकर्ता_आईडी है क्योंकि प्रत्येक उपयोगकर्ता के पास अपना स्वयं का बुकशेल्फ़ होता है जिसमें वे साइट पर कोई भी पुस्तक जोड़ सकते हैं, इसलिए उपयोगकर्ता_आईडी के साथ एक एसोसिएशन होना चाहिए ताकि प्रत्येक उपयोगकर्ता के शेल्फ में कितनी किताबें हों। विदेशी कुंजी के लिए, मैं इनके लिए माईसाम टेबल का उपयोग कर रहा हूं, इसलिए मैं एफके का उपयोग नहीं कर सकता। InnoDB और FK पर स्विच करने से प्रदर्शन में वास्तविक अंतर आएगा? – mandel

+1

विदेशी कुंजी सामान्य रूप से प्रदर्शन पर एक हिट होगी क्योंकि बाधा जांच के दौरान प्रदर्शन किया जाना चाहिए (और संभवतः अद्यतन/हटाएं)। लेकिन विशेष रूप से इस क्वेरी के साथ डेटा पुनर्प्राप्ति के लिए, मुझे कोई फर्क नहीं पड़ता क्योंकि आपके पास इंडेक्स हैं। मैं हालांकि InnoDB के लिए जाना होगा - कम से कम डेटा अखंडता के प्रयोजनों के लिए। –

2

आपको user_id पर इंडेक्स बनाने की आवश्यकता है (अधिमानतः क्लस्टर्ड इंडेक्स यदि संभव हो तो)।

क्या आप वाकई यह कर चुके हैं? याद रखें कि एक विदेशी कुंजी होने से स्वचालित रूप से उस कुंजी पर एक अनुक्रमणिका उत्पन्न नहीं होती है।

यदि आप प्रत्येक 1k पंक्तियों के 4 बी-पेड़ में शामिल हो रहे हैं, तो निश्चित रूप से 9s, लेकिन कुछ मिलीसेकंड नहीं लेना चाहिए।

लंबे निष्पादन समय इंगित करता है कि आप प्रत्येक उपयोगकर्ता के लिए टेबल स्कैन कर रहे हैं।

मुझे पूरा विश्वास है कि यह सही जवाब है।

आपकी क्वेरी ठीक है सिवाय इसके कि आप अपनी समीक्षा दो बार काउंटी कर रहे हैं, दूसरी गणना को बुकरीव्यू और numreviews के साथ बदलें।

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