MySQL में सेल्फ-जॉइनिंग टेबल के बिना कई मानों के खिलाफ एकल कॉलम का मिलान करना


14

हमारे पास एक तालिका है जिसका उपयोग हम प्रश्नों के उत्तर संग्रहीत करने के लिए करते हैं। हमें उन उपयोगकर्ताओं को खोजने में सक्षम होना चाहिए जिनके पास कुछ विशेष प्रश्नों के उत्तर हैं। इसलिए, यदि हमारी तालिका में निम्नलिखित डेटा हैं:

user_id     question_id     answer_value  
Sally        1               Pooch  
Sally        2               Peach  
John         1               Pooch  
John         2               Duke

और हम उन उपयोगकर्ताओं को खोजना चाहते हैं जो प्रश्न 1 के लिए 'पूच' और प्रश्न 2 के लिए 'पीच' का उत्तर देते हैं, निम्नलिखित एसक्यूएल इच्छाशक्ति (स्पष्ट रूप से) खराब नहीं होगी:

select user_id 
from answers 
where question_id=1 
  and answer_value = 'Pooch'
  and question_id=2
  and answer_value='Peach'

मेरा पहला विचार था कि हम जिस उत्तर की तलाश कर रहे हैं, उसके लिए तालिका में स्वयं शामिल हों:

select a.user_id 
from answers a, answers b 
where a.user_id = b.user_id
  and a.question_id=1
  and a.answer_value = 'Pooch'
  and b.question_id=2
  and b.answer_value='Peach'

यह काम करता है, लेकिन जब से हम खोज फ़िल्टर की एक मनमानी संख्या की अनुमति देते हैं, हमें कुछ और अधिक कुशल खोजने की आवश्यकता है। मेरा अगला हल कुछ इस तरह था:

select user_id, count(question_id) 
from answers 
where (
       (question_id=2 and answer_value = 'Peach') 
    or (question_id=1 and answer_value = 'Pooch')
      )
group by user_id 
having count(question_id)>1

हालाँकि, हम चाहते हैं कि उपयोगकर्ता एक ही प्रश्नावली को दो बार लेने में सक्षम हों, इसलिए वे संभावित रूप से उत्तर तालिका में प्रश्न 1 के दो उत्तर दे सकते हैं।

इसलिए, अब मैं नुकसान में हूं। इसके लिए सबसे अच्छा तरीका क्या है? धन्यवाद!

जवाबों:


8

मैंने इस क्वेरी को सेल्फ जॉइन के बिना एक चतुर तरीका पाया है।

मैंने विंडोज के लिए MySQL 5.5.8 में इन कमांडों को चलाया और निम्नलिखित परिणाम प्राप्त किए:

use test
DROP TABLE IF EXISTS answers;
CREATE TABLE answers (user_id VARCHAR(10),question_id INT,answer_value VARCHAR(20));
INSERT INTO answers VALUES
('Sally',1,'Pouch'),
('Sally',2,'Peach'),
('John',1,'Pooch'),
('John',2,'Duke');
INSERT INTO answers VALUES
('Sally',1,'Pooch'),
('Sally',2,'Peach'),
('John',1,'Pooch'),
('John',2,'Duck');

SELECT user_id,question_id,GROUP_CONCAT(DISTINCT answer_value) given_answers
FROM answers GROUP BY user_id,question_id;

+---------+-------------+---------------+
| user_id | question_id | given_answers |
+---------+-------------+---------------+
| John    |           1 | Pooch         |
| John    |           2 | Duke,Duck     |
| Sally   |           1 | Pouch,Pooch   |
| Sally   |           2 | Peach         |
+---------+-------------+---------------+

इस प्रदर्शन से पता चलता है कि जॉन ने प्रश्न 2 के दो अलग-अलग उत्तर दिए और सैली ने प्रश्न 1 के दो अलग-अलग उत्तर दिए।

सभी उपयोगकर्ताओं द्वारा अलग-अलग प्रश्नों का उत्तर देने के लिए, उपर्युक्त क्वेरी को एक उपश्रेणी में रखें और दिए गए उत्तरों की सूची में अल्पविराम के लिए अलग-अलग उत्तरों की गिनती प्राप्त करने के लिए जाँच करें:

SELECT user_id,question_id,given_answers,
(LENGTH(given_answers) - LENGTH(REPLACE(given_answers,',','')))+1 multianswer_count
FROM (SELECT user_id,question_id,GROUP_CONCAT(DISTINCT answer_value) given_answers
FROM answers GROUP BY user_id,question_id) A;

मुझे यह मिल गया:

+---------+-------------+---------------+-------------------+
| user_id | question_id | given_answers | multianswer_count |
+---------+-------------+---------------+-------------------+
| John    |           1 | Pooch         |                 1 |
| John    |           2 | Duke,Duck     |                 2 |
| Sally   |           1 | Pouch,Pooch   |                 2 |
| Sally   |           2 | Peach         |                 1 |
+---------+-------------+---------------+-------------------+

अब बस उन पंक्तियों को फ़िल्टर करें जहाँ एक और सबक्वेरी का उपयोग करके multianswer_count = 1:

SELECT * FROM (SELECT user_id,question_id,given_answers,
(LENGTH(given_answers) - LENGTH(REPLACE(given_answers,',','')))+1 multianswer_count
FROM (SELECT user_id,question_id,GROUP_CONCAT(DISTINCT answer_value) given_answers
FROM answers GROUP BY user_id,question_id) A) AA WHERE multianswer_count > 1;

मेरे पास बस यही है:

+---------+-------------+---------------+-------------------+
| user_id | question_id | given_answers | multianswer_count |
+---------+-------------+---------------+-------------------+
| John    |           2 | Duke,Duck     |                 2 |
| Sally   |           1 | Pouch,Pooch   |                 2 |
+---------+-------------+---------------+-------------------+

अनिवार्य रूप से, मैंने तीन टेबल स्कैन किए: मुख्य टेबल पर 1, 2 छोटी उपश्रेणियों पर। कोई लाभ नहीं !!!

कोशिश करो !!!


1
मैं हमेशा आपके उत्तरों में डाले गए प्रयास के स्तर की सराहना करता हूं।
बेतरतीब

7

मुझे खुद से जुड़ना पसंद है:

SELECT a.user_id FROM answers a
INNER JOIN answers a1 ON a1.question_id=1 AND a1.answer_value='Pooch'
INNER JOIN answers a2 ON a2.question_id=2 AND a2.answer_value='Peach'
GROUP BY a.user_id

अद्यतन एक बड़ी तालिका (~ 1 मिलियन पंक्तियों) के साथ परीक्षण करने के बाद, यह विधि ORमूल प्रश्न में वर्णित सरल विधि की तुलना में काफी अधिक समय लेती है ।


उत्तर के लिए धन्यवाद। मुद्दा यह है कि यह संभावित रूप से एक बड़ी तालिका हो सकती है, और 5-6 बार इसमें शामिल होने का मतलब है कि एक बड़ा प्रदर्शन हिट हो सकता है, सही है?
क्रिस्टोफर आर्मस्ट्रांग

अच्छा quesiton। मैं इसे बाहर का परीक्षण करने के लिए एक टेस्टकेस लिख रहा हूं, जैसा कि मुझे नहीं पता है ... जब यह किया जाएगा तो परिणाम पोस्ट करेंगे
डेरेक डाउनी

1
इसलिए मैंने यादृच्छिक उपयोगकर्ता, प्रश्न / उत्तर जोड़े के साथ 1 मिलियन पंक्तियाँ डालीं। Join अभी भी 557 सेकंड्स में चल रहा है और आपकी OR क्वेरी 1.84 सेकंड में समाप्त हो गई है ... अब एक कोने में बैठने जा रहा है।
डेरेक डाउनी

क्या आपके पास परीक्षण की मेज पर अनुक्रमित हैं? यदि आप लाखों पंक्तियों की तालिका को कुछ बार स्कैन कर रहे हैं तो यह थोड़ा धीमा होगा, इसमें कोई संदेह नहीं है :-)।
मैरियन

@ मेरियन हाँ, मैंने एक इंडेक्स जोड़ा (question_id, answer_value) समस्या कार्डिनैलिटी बेहद कम है, इसलिए यह बहुत मदद नहीं करता है (प्रत्येक जॉइन्ट 100-200k पंक्तियों में स्कैन किया गया था)
डेरेक डाउनी

5

हम अन्य तालिकाओं से डेटा प्राप्त करने के लिए जोड़ियों की एक श्रृंखला में तालिका user_idसे शामिल हो रहे थे answers, लेकिन उत्तर तालिका एसक्यूएल को अलग करना और इसे ऐसे सरल शब्दों में लिखना मुझे समाधान का पता लगाने में मदद करता है:

SELECT user_id, COUNT(question_id) 
FROM answers 
WHERE
  (question_id = 2 AND answer_value = 'Peach') 
  OR (question_id = 1 AND answer_value = 'Pooch')
GROUP by user_id 
HAVING COUNT(question_id) > 1

हम अनावश्यक रूप से दूसरी उप-क्वेरी का उपयोग कर रहे थे।


मैं आप का जवाब चाहते
Kisspa

4

यदि आपके पास डेटा का एक बड़ा सेट है, तो मैं दो सूचकांक करूंगा:

  • question_id, answer_value, user_id; तथा
  • user_id, question_id, answer_value।

डेटा व्यवस्थित होने के तरीके के कारण आपको कई बार जुड़ना होगा। यदि आप जानते हैं कि किस प्रश्न के लिए कौन सा मूल्य कम से कम सामान्य है तो आप क्वेरी को थोड़ा गति दे सकते हैं, लेकिन ऑप्टिमाइज़र को यह आपके लिए करना चाहिए।

क्वेरी इस रूप में आज़माएं:

A1.user_id से जवाब a1 चुनें
WH1 a1.question_id = 1 और a1.answer_value = 'Pooch'
INNER JOIN a2 ऑन a2.question_id = 2 का उत्तर देता है 
   और a2.answer_value = 'पीच' और a1.user_id = a2.user_id

तालिका 1 को पहले सूचकांक का उपयोग करना चाहिए। डेटा वितरण के आधार पर ऑप्टिमाइज़र इंडेक्स का उपयोग कर सकता है। संपूर्ण क्वेरी इंडेक्स से संतुष्ट होनी चाहिए।


2

इसे एप्रोच करने का एक तरीका यह है कि user_id का सबसेट प्राप्त करें और दूसरे मैच के लिए इनका परीक्षण करें:

SELECT user_id 
FROM answers 
WHERE question_id = 1 
AND answer_value = 'Pooch'
AND user_id IN (SELECT user_id FROM answers WHERE question_id=2 AND answer_value = 'Peach');

रोलैंडो की संरचना का उपयोग करना:

CREATE TABLE answers (user_id VARCHAR(10),question_id INT,answer_value VARCHAR(20));
INSERT INTO answers VALUES
('Sally',1,'Pouch'),
('Sally',2,'Peach'),
('John',1,'Pooch'),
('John',2,'Duke');
INSERT INTO answers VALUES
('Sally',1,'Pooch'),
('Sally',2,'Peach'),
('John',1,'Pooch'),
('John',2,'Duck');

पैदावार:

mysql> SELECT user_id FROM answers WHERE question_id = 1 AND answer_value = 'Pooch' AND user_id IN (SELECT user_id FROM answers WHERE question_id=2 AND answer_value = 'Peach');
+---------+
| user_id |
+---------+
| Sally   |
+---------+
1 row in set (0.00 sec)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.