2016-07-04 11 views
7

में उपनामों का उपयोग करने की अनुमति देने के प्रदर्शन प्रभावों ने आज this question पर अपने आप से कुछ मूर्ख बना दिया। प्रश्न SQL सर्वर का उपयोग कर रहा था, और HAVING खंड जोड़ने में सही उत्तर शामिल था। मैंने शुरू की गई प्रारंभिक गलती यह सोचने के लिए थी कि SELECT कथन में उपनाम HAVING खंड में उपयोग किया जा सकता है, जिसे SQL सर्वर में अनुमति नहीं है। मैंने यह त्रुटि बनाई क्योंकि मुझे लगता है कि SQL सर्वर के पास MySQL के समान नियम थे, जो HAVING खंड में उपनाम का उपयोग करने की अनुमति देता है।हैविंग क्लॉज

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

एक ठोस उदाहरण के लिए, मैं क्वेरी जो उपर्युक्त प्रश्न में हुई नकल होगा:

SELECT students.camID, campus.camName, COUNT(students.stuID) as studentCount 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING COUNT(students.stuID) > 3 
ORDER BY studentCount 

क्या बजाय HAVING खंड में एक उपनाम का उपयोग कर रहे निर्दिष्ट करने के प्रदर्शन निहितार्थ होगा COUNT? यह प्रश्न सीधे MySQL में उत्तर दिया जा सकता है, और उम्मीद है कि कोई व्यक्ति एसक्यूएल में क्या होगा, अगर HAVING खंड में उपनाम का समर्थन करना था।

यह एक दुर्लभ उदाहरण है जहां एक SQL प्रश्न को MySQL और SQL सर्वर दोनों के साथ टैग करना ठीक हो सकता है, इसलिए इस क्षण का आनंद सूर्य में लें।

+1

के लिए, इस समय होने में उर्फ ​​के पक्ष में दिखाई देता है। – Drew

+1

संभावित डुप्लिकेट [खंड में एक एसक्यूएल पैरामीटरेट करें] (http://stackoverflow.com/questions/337704/parameterize-an-sql-in-clause) –

उत्तर

3

संक्षेप में उस विशेष क्वेरी पर ध्यान केंद्रित किया गया, और नमूना डेटा नीचे लोड किया गया। यह कुछ अन्य प्रश्नों को संबोधित करता है जैसे कि count(distinct ...) दूसरों द्वारा उल्लिखित।

alias in the HAVING या तो थोड़ा बेहतर प्रदर्शन करता है या इसके वैकल्पिक (क्वेरी के आधार पर) थोड़ा अधिक प्रदर्शन करता है।

यह एक पूर्व-मौजूदा तालिका का उपयोग करता है जिसमें लगभग 5 मिलियन पंक्तियां होती हैं जो इस answer के माध्यम से जल्दी से बनाई जाती है जिसमें 3 से 5 मिनट लगते हैं।

परिणामस्वरूप संरचना:

CREATE TABLE `ratings` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `thing` int(11) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8; 

लेकिन बजाय InnoDB का उपयोग कर। रेंज आरक्षण आवेषण के कारण अपेक्षित आईएनएनओडीबी अंतर विसंगति उत्पन्न करता है। बस कह रहा है, लेकिन कोई फर्क नहीं पड़ता। 4.7 मिलियन पंक्तियां

टिम की अनुमानित स्कीमा के पास जाने के लिए तालिका को संशोधित करें।

rename table ratings to students; -- not exactly instanteous (a COPY) 
alter table students add column camId int; -- get it near Tim's schema 
-- don't add the `camId` index yet 

निम्नलिखित में कुछ समय लगेगा। इसे बार-बार भाग में चलाएं या फिर आपका कनेक्शन टाइमआउट हो सकता है। अपडेट स्टेटमेंट में LIMIT क्लॉज के बिना टाइमआउट 5 मिलियन पंक्तियों के कारण है। नोट, हम में एक LIMIT क्लॉज है।

तो हम इसे आधे मिलियन पंक्ति पुनरावृत्तियों में कर रहे हैं। 1 और 20

update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise) 

जो यादृच्छिक संख्या के लिए एक स्तंभ सेट से ऊपर चल रहा है जब तक कोई camId रिक्त है रखें।

मैं (पूरी बात 7 से 10 मिनट लगते हैं)

select camId,count(*) from students 
group by camId order by 1 ; 

1 235641 
2 236060 
3 236249 
4 235736 
5 236333 
6 235540 
7 235870 
8 236815 
9 235950 
10 235594 
11 236504 
12 236483 
13 235656 
14 236264 
15 236050 
16 236176 
17 236097 
18 235239 
19 235556 
20 234779 

select count(*) from students; 
-- 4.7 Million rows 

(निश्चित रूप से आवेषण के बाद) एक उपयोगी सूचकांक बनाएं 10 बार की तरह यह भाग गया।

create index `ix_stu_cam` on students(camId); -- takes 45 seconds 

ANALYZE TABLE students; -- update the stats: http://dev.mysql.com/doc/refman/5.7/en/analyze-table.html 
-- the above is fine, takes 1 second 

परिसर तालिका बनाएं।

create table campus 
( camID int auto_increment primary key, 
    camName varchar(100) not null 
); 
insert campus(camName) values 
('one'),('2'),('3'),('4'),('5'), 
('6'),('7'),('8'),('9'),('ten'), 
('etc'),('etc'),('etc'),('etc'),('etc'), 
('etc'),('etc'),('etc'),('etc'),('twenty'); 
-- ok 20 of them 

भागो दो प्रश्नों:

SELECT students.camID, campus.camName, COUNT(students.id) as studentCount 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING COUNT(students.id) > 3 
ORDER BY studentCount; 
-- run it many many times, back to back, 5.50 seconds, 20 rows of output 

और

SELECT students.camID, campus.camName, COUNT(students.id) as studentCount 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING studentCount > 3 
ORDER BY studentCount; 
-- run it many many times, back to back, 5.50 seconds, 20 rows of output 

तो बार समान हैं। हर एक दर्जन बार दौड़ें।

EXPLAIN उत्पादन दोनों

+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra       | 
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+ 
| 1 | SIMPLE  | campus | ALL | PRIMARY  | NULL  | NULL | NULL     |  20 | Using temporary; Using filesort | 
| 1 | SIMPLE  | students | ref | ix_stu_cam | ix_stu_cam | 5  | bigtest.campus.camID | 123766 | Using index      | 
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+ 

औसत() फ़ंक्शन का उपयोग करने से, मैं एक 12% प्रदर्शन में उर्फ ​​साथ having में (समान EXPLAIN उत्पादन के साथ) वृद्धि के बारे में हो रही है के लिए एक ही है दो प्रश्नों का पालन करें।

SELECT students.camID, campus.camName, avg(students.id) as studentAvg 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING avg(students.id) > 2200000 
ORDER BY students.camID; 
-- avg time 7.5 

explain 

SELECT students.camID, campus.camName, avg(students.id) as studentAvg 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING studentAvg > 2200000 
ORDER BY students.camID; 
-- avg time 6.5 

और अंत में, DISTINCT:

SELECT students.camID, count(distinct students.id) as studentDistinct 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID 
HAVING count(distinct students.id) > 1000000 
ORDER BY students.camID; -- 10.6 10.84 12.1 11.49 10.1 9.97 10.27 11.53 9.84 9.98 
-- 9.9 

SELECT students.camID, count(distinct students.id) as studentDistinct 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID 
HAVING studentDistinct > 1000000 
ORDER BY students.camID; -- 6.81 6.55 6.75 6.31 7.11 6.36 6.55 
-- 6.45 

उर्फ ​​होने में लगातार 35% ही EXPLAIN उत्पादन के साथ तेजी से चलाता है। नीचे देखा गया। तो वही स्पष्टीकरण आउटपुट दो बार दिखाया गया है न कि एक ही प्रदर्शन में, बल्कि एक सामान्य सुराग के रूप में।

+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra          | 
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | campus | index | PRIMARY  | PRIMARY | 4  | NULL     |  20 | Using index; Using temporary; Using filesort | 
| 1 | SIMPLE  | students | ref | ix_stu_cam | ix_stu_cam | 5  | bigtest.campus.camID | 123766 | Using index         | 
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+ 

अनुकूलक मैं अपने आप को के बाहर हर समय एक मूर्ख बनाने के लिए विशेष रूप से DISTINCT.

2

यह एक टिप्पणी के लिए बहुत लंबा है।

मुझे नहीं लगता कि वास्तव में कोई प्रदर्शन प्रभाव नहीं है, जब तक कि having खंड में अभिव्यक्ति जटिल प्रसंस्करण (कहें, count(distinct) या एक जटिल कार्य, जैसे लंबी स्ट्रिंग पर स्ट्रिंग प्रोसेसिंग) शामिल है।

मुझे लगभग निश्चित है कि MySQL क्वेरी में दो बार उल्लिखित होने पर दो बार एग्रीगेशन फ़ंक्शन निष्पादित करेगा। मुझे यकीन नहीं है कि क्या SQL सर्वर दूसरे संदर्भ को ऑप्टिमाइज़ करेगा, लेकिन मुझे लगता है कि नहीं (SQL सर्वर का एक अच्छा अनुकूलक है लेकिन यह एक सामान्य अभिव्यक्ति उन्मूलन इतना अच्छा नहीं है)।

प्रश्न तब अभिव्यक्ति की जटिलता है। सरल अभिव्यक्ति जैसे कि count() और sum() वास्तव में अधिक अतिरिक्त ओवरहेड नहीं लेते - एक बार एकत्रीकरण पहले ही हो रहा है। जटिल अभिव्यक्ति महंगा हो सकती है।

यदि आपके पास SQL ​​सर्वर में एक जटिल अभिव्यक्ति है, तो आपको यह गारंटी देने में सक्षम होना चाहिए कि यह केवल एक बार सबक्वायरी का उपयोग करके मूल्यांकन किया जाता है।

+0

तो यह कहना सुरक्षित होगा कि यह आमतौर पर MySQL में बेहतर है 'हैविंग' खंड में _use_ एक उपनाम, मानते हुए ऐसा करना संभव है? –

+0

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

+0

@TimBiegeleisen। । । हाँ मैं सहमत हूँ। मुझे पूरा यकीन है कि 'होने' खंड में उपनाम को आंशिक रूप से इस तथ्य पर आधारित है कि MySQL subqueries को पूरा करता है। इसलिए, जब आप इसे फ़िल्टरिंग के लिए उपयोग करना चाहते हैं तो जटिल गणना (जैसे दूरी) को परिभाषित करने के लिए एक सबक्वायरी एक अच्छा तरीका नहीं है। –

1

मैं एसक्यूएल उम्मीद कर रहा था FROM, WHERE, GROUP BY, HAVING, SELECT के क्रम में आगे बढ़ने के लिए, ORDER BY

मैं ने MySQL विशेषज्ञ नहीं हूँ, लेकिन यह बाहर कारण है कि यह कानूनी है पर MYSQL Documentation में कारण पाया ।

MySQL ग्रुप BY के मानक SQL उपयोग को बढ़ाता है ताकि चयन सूची ग्रुप बाय क्लॉज में नामित गैर-समेकित कॉलम का संदर्भ दे सके। इसका मतलब है कि पिछली क्वेरी MySQL में कानूनी है। अनावश्यक कॉलम सॉर्टिंग और ग्रुपिंग से बचकर बेहतर प्रदर्शन प्राप्त करने के लिए आप इस सुविधा का उपयोग कर सकते हैं। हालांकि, यह प्राथमिक रूप से उपयोगी है जब ग्रुप BY में नामित प्रत्येक गैर-समेकित कॉलम में सभी मान प्रत्येक समूह के लिए समान नहीं हैं। सर्वर प्रत्येक समूह से किसी भी मूल्य का चयन करने के लिए स्वतंत्र है, इसलिए जब तक वे समान नहीं हैं, तो चुने गए मान अनिश्चित हैं। इसके अलावा, प्रत्येक समूह के मूल्यों का चयन खंड द्वारा ऑर्डर जोड़कर प्रभावित नहीं हो सकता है। परिणाम सेट सॉर्टिंग मानों के चयन के बाद होती है, और ORDER BY सर्वर को चुनने वाले प्रत्येक समूह के भीतर कौन से मानों को प्रभावित नहीं करता है।

एक समान MySQL एक्सटेंशन HAVING क्लॉज पर लागू होता है। मानक एसक्यूएल में, एक क्वेरी हैविंग क्लॉज में गैर-समेकित कॉलम का संदर्भ नहीं दे सकती है जिसे ग्रुप बाय क्लॉज में नामित नहीं किया गया है। गणना को सरल बनाने के लिए, एक MySQL एक्सटेंशन ऐसे कॉलम के संदर्भों की अनुमति देता है। यह एक्सटेंशन मानता है कि गैर-समूहित कॉलम में समान समूह-वार मान हैं। अन्यथा, परिणाम अनिश्चित है।

प्रदर्शन प्रभाव पर, मुझे लगता है कि, एलियाज्ड असंगत होने से धीमा हो जाएगा क्योंकि फ़िल्टर को सभी निष्पादन के बाद लागू किया जाना चाहिए। विशेषज्ञों की टिप्पणी करने के लिए मैं इंतजार करूंगा।