मुझे विश्वास नहीं है कि कोई ग्रुप BY आपको वह परिणाम देगा जो आप चाहते हैं। और दुर्भाग्य से, MySQL विश्लेषणात्मक कार्यों का समर्थन नहीं करता है (जिस तरह से हम Oracle या SQL सर्वर में इस समस्या को हल करेंगे।)
उपयोगकर्ता-परिभाषित चरों का उपयोग करके, कुछ अल्पविकसित विश्लेषणात्मक कार्यों का अनुकरण करना संभव है।
इस मामले में, हम अनुकरण करना चाहते हैं:
ROW_NUMBER() OVER(PARTITION BY doctor_id ORDER BY distance ASC) AS seq
इसलिए, मूल क्वेरी से शुरू करते हुए, मैंने ORDER BY को बदल दिया ताकि यह doctor_id
पर छा जाए पहले, और फिर परिकलित distance
. पर . (जब तक हम उन दूरियों को नहीं जानते, हम नहीं जानते कि कौन सी "निकटतम" है।)
इस क्रमबद्ध परिणाम के साथ, हम मूल रूप से प्रत्येक डॉक्टर_आईडी के लिए पंक्तियों को "संख्या" करते हैं, निकटतम 1 के रूप में, दूसरा निकटतम 2, और इसी तरह। जब हमें एक नया डॉक्टर_आईडी मिलता है, तो हम फिर से सबसे निकटतम 1 से शुरू करते हैं।
इसे पूरा करने के लिए, हम उपयोगकर्ता द्वारा परिभाषित चर का उपयोग करते हैं। हम पंक्ति संख्या निर्दिष्ट करने के लिए एक का उपयोग करते हैं (चर नाम @i है, और लौटाए गए कॉलम में उपनाम seq है)। दूसरी वेरिएबल का उपयोग हम पिछली पंक्ति से डॉक्टर_आईडी को "याद" करने के लिए करते हैं, ताकि हम डॉक्टर_आईडी में "ब्रेक" का पता लगा सकें, ताकि हम जान सकें कि पंक्ति नंबरिंग को फिर से 1 पर कब फिर से शुरू करना है।
ये रही क्वेरी:
SELECT z.*
, @i := CASE WHEN z.doctor_id = @prev_doctor_id THEN @i + 1 ELSE 1 END AS seq
, @prev_doctor_id := z.doctor_id AS prev_doctor_id
FROM
(
/* original query, ordered by doctor_id and then by distance */
SELECT zip,
( 3959 * acos( cos( radians(34.12520) ) * cos( radians( zip_info.latitude ) ) * cos(radians( zip_info.longitude ) - radians(-118.29200) ) + sin( radians(34.12520) ) * sin( radians( zip_info.latitude ) ) ) ) AS distance,
user_info.*, office_locations.*
FROM zip_info
RIGHT JOIN office_locations ON office_locations.zipcode = zip_info.zip
RIGHT JOIN user_info ON office_locations.doctor_id = user_info.id
WHERE user_info.status='yes'
ORDER BY user_info.doctor_id ASC, distance ASC
) z JOIN (SELECT @i := 0, @prev_doctor_id := NULL) i
HAVING seq = 1 ORDER BY z.distance
मैं एक धारणा बना रहा हूं कि मूल क्वेरी आपको आवश्यक परिणाम सेट लौटा रही है, इसमें बहुत अधिक पंक्तियां हैं, और आप प्रत्येक डॉक्टर_आईडी के लिए "निकटतम" (दूरी के न्यूनतम मूल्य वाली पंक्ति) को छोड़कर सभी को खत्म करना चाहते हैं।
मैंने आपकी मूल क्वेरी को किसी अन्य क्वेरी में लपेट लिया है; मैंने मूल क्वेरी में केवल यही परिवर्तन किया था कि परिणामों को डॉक्टर_आईडी और फिर दूरी के आधार पर क्रमबद्ध किया जाए, और HAVING distance < 50
को हटाया जाए। खंड। (यदि आप केवल 50 से कम दूरी वापस करना चाहते हैं, तो आगे बढ़ें और उस खंड को वहीं छोड़ दें। यह स्पष्ट नहीं था कि क्या यह आपका इरादा था, या क्या यह पंक्तियों को प्रति डॉक्टर_आईडी तक सीमित करने के प्रयास में निर्दिष्ट किया गया था।)पी>
ध्यान देने योग्य कुछ मुद्दे:
प्रतिस्थापन क्वेरी दो अतिरिक्त कॉलम लौटाती है; परिणाम सेट को उत्पन्न करने के साधनों को छोड़कर, परिणाम सेट में इनकी वास्तव में आवश्यकता नहीं होती है। (उन कॉलम को छोड़ने के लिए इस पूरे चयन को फिर से किसी अन्य चयन में लपेटना संभव है, लेकिन यह वास्तव में इसके लायक से अधिक गन्दा है। मैं केवल कॉलम पुनर्प्राप्त करूंगा, और जानता हूं कि मैं उन्हें अनदेखा कर सकता हूं।)
दूसरा मुद्दा यह है कि .*
. का उपयोग आंतरिक क्वेरी में थोड़ा खतरनाक है, इसमें हमें वास्तव में गारंटी देने की ज़रूरत है कि उस क्वेरी द्वारा लौटाए गए कॉलम नाम अद्वितीय हैं। (भले ही कॉलम नाम अभी अलग हैं, उन तालिकाओं में से एक में कॉलम जोड़ने से क्वेरी में "अस्पष्ट" कॉलम अपवाद हो सकता है। इससे बचना सबसे अच्छा है, और इसे आसानी से .*
लौटाए जाने वाले कॉलम की सूची के साथ, और किसी भी "डुप्लिकेट" कॉलम नाम के लिए उपनाम निर्दिष्ट करना। (z.*
. का उपयोग बाहरी क्वेरी में कोई चिंता नहीं है, जब तक कि हम z
द्वारा लौटाए गए कॉलम के नियंत्रण में हैं ।)
परिशिष्ट:
मैंने नोट किया कि GROUP BY आपको वह परिणाम सेट नहीं देगा जिसकी आपको आवश्यकता है। हालांकि GROUP BY का उपयोग करके एक क्वेरी के साथ परिणाम सेट प्राप्त करना संभव होगा, एक कथन जो CORRECT परिणाम सेट लौटाता है वह थकाऊ होगा। आप निर्दिष्ट कर सकते हैं MIN(distance) ... GROUP BY doctor_id
, और इससे आपको सबसे छोटी दूरी मिल जाएगी, लेकिन इस बात की कोई गारंटी नहीं है कि चयन सूची में अन्य गैर-समग्र भाव न्यूनतम दूरी वाली पंक्ति से होंगे, न कि किसी अन्य पंक्ति से। (MySQL ग्रुप बाय और एग्रीगेट्स के संबंध में खतरनाक रूप से उदार है। MySQL इंजन को अधिक सतर्क रहने के लिए (और अन्य रिलेशनल डेटाबेस इंजनों के अनुरूप), SET sql_mode = ONLY_FULL_GROUP_BY
परिशिष्ट 2:
डेरियस द्वारा रिपोर्ट की गई प्रदर्शन समस्याएं "कुछ क्वेरी में 7 सेकंड लगते हैं।"
चीजों को गति देने के लिए, आप शायद फ़ंक्शन के परिणामों को कैश करना चाहते हैं। मूल रूप से, एक लुकअप टेबल बनाएं। उदा.
CREATE TABLE office_location_distance
( office_location_id INT UNSIGNED NOT NULL COMMENT 'PK, FK to office_location.id'
, zipcode_id INT UNSIGNED NOT NULL COMMENT 'PK, FK to zipcode.id'
, gc_distance DECIMAL(18,2) COMMENT 'calculated gc distance, in miles'
, PRIMARY KEY (office_location_id, zipcode_id)
, KEY (zipcode_id, gc_distance, office_location_id)
, CONSTRAINT distance_lookup_office_FK
FOREIGN KEY (office_location_id) REFERENCES office_location(id)
ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT distance_lookup_zipcode_FK
FOREIGN KEY (zipcode_id) REFERENCES zipcode(id)
ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB
यह सिर्फ एक विचार है। (मुझे उम्मीद है कि आप किसी विशेष ज़िपकोड से ऑफिस_लोकेशन दूरी की खोज कर रहे हैं, इसलिए इंडेक्स ऑन (ज़िपकोड, जीसी_डिस्टेंस, ऑफिस_लोकेशन_आईडी) वह कवरिंग इंडेक्स है जिसकी आपकी क्वेरी को आवश्यकता होगी। (मैं खराब होने के कारण गणना की गई दूरी को FLOAT के रूप में संग्रहीत करने से बचूंगा) FLOAT डेटाटाइप के साथ क्वेरी प्रदर्शन)
INSERT INTO office_location_distance (office_location_id, zipcode_id, gc_distance)
SELECT d.office_location_id
, d.zipcode_id
, d.gc_distance
FROM (
SELECT l.id AS office_location_id
, z.id AS zipcode_id
, ROUND( <glorious_great_circle_calculation> ,2) AS gc_distance
FROM office_location l
CROSS
JOIN zipcode z
ORDER BY 1,3
) d
ON DUPLICATE KEY UPDATE gc_distance = VALUES(gc_distance)
फ़ंक्शन के परिणाम कैश्ड और अनुक्रमित होने के साथ, आपकी क्वेरी बहुत तेज़ होनी चाहिए।
SELECT d.gc_distance, o.*
FROM office_location o
JOIN office_location_distance d ON d.office_location_id = o.id
WHERE d.zipcode_id = 63101
AND d.gc_distance <= 100.00
ORDER BY d.zipcode_id, d.gc_distance
मैं कैश टेबल में INSERT/UPDATE पर HAVING विधेय जोड़ने में संकोच कर रहा हूं; (यदि आपके पास गलत अक्षांश/देशांतर था, और 100 मील के नीचे एक गलत दूरी की गणना की थी; लेट/लॉन्ग तय होने के बाद एक बाद की दौड़ और दूरी 1000 मील तक काम करती है ... यदि पंक्ति को क्वेरी से बाहर रखा गया है, तो कैश टेबल में मौजूदा पंक्ति अपडेट नहीं होगी। (आप कैश टेबल को साफ़ कर सकते हैं, लेकिन यह वास्तव में आवश्यक नहीं है, यह डेटाबेस और लॉग के लिए बहुत अधिक अतिरिक्त काम है। यदि रखरखाव क्वेरी का परिणाम सेट भी है बड़ा, इसे प्रत्येक ज़िप कोड, या प्रत्येक कार्यालय_स्थान के लिए पुनरावृत्त रूप से चलाने के लिए तोड़ा जा सकता है।)
दूसरी ओर, यदि आप किसी निश्चित मान से अधिक दूरी में रुचि नहीं रखते हैं, तो आप HAVING gc_distance <
जोड़ सकते हैं भविष्यवाणी करें, और कैशे टेबल के आकार को काफी कम कर दें।