Mysql
 sql >> डेटाबेस >  >> RDS >> Mysql

डिस्टिंक्ट बनाम ग्रुप बाय

आमतौर पर DISTINCT . का उपयोग करने की सलाह दी जाती है GROUP BY . के बजाय , चूंकि आप वास्तव में यही चाहते हैं, और अनुकूलक को "सर्वश्रेष्ठ" निष्पादन योजना चुनने दें। हालांकि - कोई भी अनुकूलक सही नहीं है। DISTINCT . का उपयोग करना एक निष्पादन योजना के लिए अनुकूलक के पास अधिक विकल्प हो सकते हैं। लेकिन इसका मतलब यह भी है कि उसके पास खराब योजना चुनने के विकल्प . हैं ।

आप लिखते हैं कि DISTINCT क्वेरी "धीमी" है, लेकिन आप कोई संख्या नहीं बताते हैं। मेरे परीक्षण में (MariaDB 10.0.19 . पर 10 गुना अधिक पंक्तियों के साथ) और 10.3.13 ) DISTINCT क्वेरी (केवल) 25% धीमी (562ms/453ms) की तरह है। EXPLAIN परिणाम बिल्कुल मदद नहीं है। यह "झूठ" भी है। LIMIT 100, 30 . के साथ इसे कम से कम 130 पंक्तियों को पढ़ना होगा (यही मेरा EXPLAIN है वास्तव में GROUP BY . के लिए स्को करता है ), लेकिन यह आपको 65 दिखाता है।

मैं निष्पादन समय में 25% अंतर की व्याख्या नहीं कर सकता, लेकिन ऐसा लगता है कि इंजन किसी भी मामले में एक पूर्ण तालिका/अनुक्रमणिका स्कैन कर रहा है, और परिणाम को 100 छोड़ने और 30 पंक्तियों का चयन करने से पहले क्रमबद्ध करता है।

शायद सबसे अच्छी योजना होगी:

  • idx_reg_date से पंक्तियां पढ़ें अनुक्रमणिका (तालिका A ) एक के बाद एक अवरोही क्रम में
  • देखें कि idx_order_id में कोई मेल है या नहीं अनुक्रमणिका (तालिका B )
  • 100 मिलती-जुलती पंक्तियों को छोड़ें
  • 30 मिलती-जुलती पंक्तियां भेजें
  • बाहर निकलें

अगर A . में समान 10% पंक्तियाँ हैं जिनका B . में कोई मेल नहीं है , यह योजना A . से 143 पंक्तियों की तरह कुछ पढ़ेगी ।

इस योजना को किसी भी तरह से लागू करने के लिए सबसे अच्छा मैं यह कर सकता हूं:

SELECT A.id
FROM `order` A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30
OFFSET 100

यह क्वेरी 156 ms (GROUP BY . से 3 गुना तेज) में वही परिणाम देती है ) लेकिन यह अभी भी बहुत धीमा है। और यह शायद अभी भी तालिका में सभी पंक्तियों को पढ़ रहा है A

हम साबित कर सकते हैं कि एक "छोटी" सबक्वेरी ट्रिक के साथ एक बेहतर योजना मौजूद हो सकती है:

SELECT A.id
FROM (
    SELECT id, reg_date
    FROM `order`
    ORDER BY reg_date DESC
    LIMIT 1000
) A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30
OFFSET 100

यह क्वेरी "नो टाइम" (~ 0 एमएस) में निष्पादित होती है और मेरे परीक्षण डेटा पर वही परिणाम देती है। और हालांकि यह 100% विश्वसनीय नहीं है, यह दर्शाता है कि अनुकूलक अच्छा काम नहीं कर रहा है।

तो मेरे निष्कर्ष क्या हैं:

  • ऑप्टिमाइज़र हमेशा सबसे अच्छा काम नहीं करता है और कभी-कभी मदद की ज़रूरत होती है
  • जब हम "सर्वश्रेष्ठ योजना" जानते हैं, तब भी हम इसे हमेशा लागू नहीं कर सकते हैं
  • DISTINCT GROUP BY . से हमेशा तेज़ नहीं होता है
  • जब सभी खंडों के लिए कोई अनुक्रमणिका का उपयोग नहीं किया जा सकता है - चीजें काफी मुश्किल होती जा रही हैं

टेस्ट स्कीमा और डमी डेटा:

drop table if exists `order`;
CREATE TABLE `order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `reg_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_reg_date` (`reg_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

insert into `order`(reg_date)
    select from_unixtime(floor(rand(1) * 1000000000)) as reg_date
    from information_schema.COLUMNS a
       , information_schema.COLUMNS b
    limit 218860;

drop table if exists `order_detail_products`;
CREATE TABLE `order_detail_products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `order_id` bigint(20) unsigned NOT NULL,
  `order_detail_id` int(11) NOT NULL,
  `prod_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_order_detail_id` (`order_detail_id`,`prod_id`),
  KEY `idx_order_id` (`order_id`,`order_detail_id`,`prod_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

insert into order_detail_products(id, order_id, order_detail_id, prod_id)
    select null as id
    , floor(rand(2)*218860)+1 as order_id
    , 0 as order_detail_id
    , 0 as prod_id
    from information_schema.COLUMNS a
       , information_schema.COLUMNS b
    limit 437320;

प्रश्न:

SELECT DISTINCT A.id
FROM `order` A
JOIN order_detail_products B ON A.id = B.order_id
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- 562 ms

SELECT A.id
FROM `order` A
JOIN order_detail_products B ON A.id = B.order_id
GROUP BY A.id
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- 453 ms

SELECT A.id
FROM `order` A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- 156 ms

SELECT A.id
FROM (
    SELECT id, reg_date
    FROM `order`
    ORDER BY reg_date DESC
    LIMIT 1000
) A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- ~ 0 ms



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. डुप्लीकेट कुंजी के साथ तैयार विवरण

  2. .NET . में एक MySQL डेटाबेस से कनेक्ट करना

  3. MYSQL तालिका के सबसेट के भीतर ऑटो-इन्क्रीमेंट कैसे करें

  4. रूबी ऑन रेल्स के साथ MySQL सर्वर चला गया है

  5. क्या MySQL का उपयोग करके एकाधिक कॉलम द्वारा ग्रुप करना संभव है?