จับคู่คอลัมน์เดี่ยวกับค่าหลายค่าโดยไม่มีตารางการเข้าร่วมด้วยตนเองใน MySQL


14

เรามีตารางที่เราใช้เก็บคำตอบของคำถาม เราจำเป็นต้องสามารถค้นหาผู้ใช้ที่มีคำตอบบางคำถาม ดังนั้นหากตารางของเราประกอบด้วยข้อมูลต่อไปนี้:

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

และเราต้องการค้นหาผู้ใช้ที่ตอบว่า 'Pooch' สำหรับคำถามที่ 1 และ 'Peach' สำหรับคำถามที่ 2 SQL จะดังต่อไปนี้ (ชัด) ไม่ต้องกังวล:

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 สำหรับ Windows และได้ผลลัพธ์ดังต่อไปนี้:

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
ฉันขอขอบคุณระดับความพยายามที่คุณตอบไว้
Randomx

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 ครั้งอาจหมายถึงการได้รับผลกระทบอย่างมากใช่ไหม?
Christopher Armstrong

quesiton ที่ดี ฉันกำลังเขียน testcase เพื่อทดสอบเพราะฉันไม่รู้ ... จะโพสต์ผลลัพธ์เมื่อเสร็จแล้ว
Derek Downey

1
ดังนั้นฉันจึงแทรก 1 ล้านแถวกับผู้ใช้คู่คำถาม / คำตอบแบบสุ่ม การเข้าร่วมยังคงดำเนินต่อไปที่ 557 วินาทีและการค้นหา OR ของคุณเสร็จสิ้นใน 1.84 วินาที ... กำลังจะเข้ามุม
Derek Downey

คุณมีดัชนีในตารางทดสอบหรือไม่ หากคุณกำลังสแกนตารางล้านแถวไม่กี่ครั้งมันจะช้าไปหน่อยไม่ต้องสงสัยเลย :-)
Marian

@Marian ใช่ฉันเพิ่มดัชนีใน (question_id, answer_value) ปัญหาคือ cardinality ต่ำมากดังนั้นจึงไม่ได้ช่วยอะไรมาก (การเข้าร่วมแต่ละครั้งถูกสแกน 100-200k แถว)
Derek Downey

5

เรากำลังเข้าร่วมuser_idจากanswersตารางในกลุ่มของการรวมเพื่อรับข้อมูลจากตารางอื่น ๆ แต่การแยกตารางคำตอบ SQL และการเขียนในคำศัพท์ง่าย ๆ ช่วยให้ฉันเห็นวิธีแก้ปัญหา:

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
WHERE a1.question_id = 1 และ a1.answer_value = 'Pooch'
เข้าร่วมภายในตอบ a2 ใน a2.question_id = 2 
   AND a2.answer_value = 'Peach' และ a1.user_id = a2.user_id

ตาราง a1 ควรใช้ดัชนีแรก เครื่องมือเพิ่มประสิทธิภาพอาจใช้ดัชนีอย่างใดอย่างหนึ่งทั้งนี้ขึ้นอยู่กับการกระจายข้อมูล แบบสอบถามทั้งหมดควรเป็นที่พอใจจากดัชนี


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');

ใช้โครงสร้างของ Rolando:

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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.