आमतौर पर 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