Kết hợp cột đơn với nhiều giá trị mà không cần bảng tự tham gia trong MySQL


14

Chúng tôi có một bảng mà chúng tôi sử dụng để lưu trữ câu trả lời cho các câu hỏi. Chúng tôi cần có khả năng tìm thấy những người dùng có câu trả lời nhất định cho các câu hỏi cụ thể. Vì vậy, nếu bảng của chúng tôi bao gồm các dữ liệu sau:

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

và chúng tôi muốn tìm người dùng trả lời 'Pooch' cho câu hỏi 1 và 'Peach' cho câu hỏi 2, SQL sau đây (rõ ràng) sẽ không lo lắng:

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

Suy nghĩ đầu tiên của tôi là tự tham gia vào bảng cho mỗi câu trả lời mà chúng tôi đang tìm kiếm:

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'

Điều này hoạt động, nhưng vì chúng tôi cho phép số lượng bộ lọc tìm kiếm tùy ý, chúng tôi cần tìm thứ gì đó hiệu quả hơn nhiều. Giải pháp tiếp theo của tôi là một cái gì đó như thế này:

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

Tuy nhiên, chúng tôi muốn người dùng có thể thực hiện cùng một câu hỏi hai lần, vì vậy họ có thể có hai câu trả lời cho câu hỏi 1 trong bảng câu trả lời.

Vì vậy, bây giờ tôi đang thua lỗ. Cách tốt nhất để tiếp cận điều này là gì? Cảm ơn!

Câu trả lời:


8

Tôi đã tìm thấy một cách thông minh để thực hiện truy vấn này mà không cần tự tham gia.

Tôi đã chạy các lệnh này trong MySQL 5.5.8 cho Windows và nhận được kết quả như sau:

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         |
+---------+-------------+---------------+

Màn hình này cho thấy John đã đưa ra hai câu trả lời khác nhau cho câu hỏi 2 và Sally đã đưa ra hai câu trả lời khác nhau cho câu hỏi 1.

Để nắm bắt những câu hỏi nào được trả lời khác nhau bởi tất cả người dùng, chỉ cần đặt truy vấn trên vào truy vấn con và kiểm tra dấu phẩy trong danh sách các câu trả lời đã cho để có được số lượng câu trả lời khác nhau như sau:

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;

Tôi hiểu rồi:

+---------+-------------+---------------+-------------------+
| 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 |
+---------+-------------+---------------+-------------------+

Bây giờ chỉ cần lọc ra các hàng trong đó multianswer_count = 1 bằng cách sử dụng truy vấn con khác:

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;

Đây là những gì tôi nhận được:

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

Về cơ bản, tôi đã thực hiện ba lần quét bảng: 1 trên bảng chính, 2 trên các truy vấn nhỏ. KHÔNG THAM GIA !!!

Hãy thử một lần !!!


1
Tôi luôn đánh giá cao mức độ nỗ lực của bạn vào câu trả lời của bạn.
Randomx

7

Tôi thích phương pháp tham gia, bản thân mình:

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

Cập nhật Sau khi thử nghiệm với bảng lớn hơn (~ 1 triệu hàng), phương pháp này mất nhiều thời gian hơn so với ORphương pháp đơn giản được đề cập trong câu hỏi ban đầu.


Cảm ơn vi đa trả lơi. Vấn đề là điều này có khả năng có thể là một bảng lớn, và phải tham gia nó 5-6 lần có thể có nghĩa là đạt được thành tích lớn, đúng không?
Christopher Armstrong

quesiton tốt. Tôi đang viết một bản thử nghiệm để kiểm tra, vì tôi không biết ... sẽ đăng kết quả khi hoàn thành
Derek Downey

1
vì vậy tôi đã chèn 1 triệu hàng với cặp người dùng, câu hỏi / câu trả lời ngẫu nhiên. Tham gia vẫn diễn ra ở 557 giây và truy vấn OR của bạn kết thúc sau 1,84 giây ... sẽ ngồi ở một góc ngay bây giờ.
Derek Downey

Bạn có chỉ số trên bảng thử nghiệm? Nếu bạn đang quét hàng triệu bảng hàng vài lần thì sẽ hơi chậm, không còn nghi ngờ gì nữa :-).
Mary

@Mary yeah, tôi đã thêm một chỉ mục cho vấn đề (question_id, answer_value) là số lượng thẻ rất thấp, vì vậy nó không giúp được gì nhiều (mỗi lần tham gia được quét 100-200k hàng)
Derek Downey

5

Chúng tôi đã tham gia user_idtừ answersbảng trong một chuỗi các phép nối để lấy dữ liệu từ các bảng khác, nhưng cách ly bảng câu trả lời SQL và viết nó bằng các thuật ngữ đơn giản như vậy đã giúp tôi phát hiện ra giải pháp:

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

Chúng tôi không cần thiết sử dụng truy vấn phụ thứ hai.


tôi thích bạn trả lời
Kisspa

4

Nếu bạn có một bộ dữ liệu lớn, tôi sẽ thực hiện hai chỉ mục:

  • question_id, answer_value, user_id; và
  • user_id, question_id, answer_value.

Bạn sẽ cần phải tham gia nhiều lần vì cách thức tổ chức dữ liệu. Nếu bạn biết giá trị nào cho câu hỏi ít phổ biến nhất, bạn có thể tăng tốc truy vấn một chút, nhưng trình tối ưu hóa sẽ làm điều đó cho bạn.

Hãy thử truy vấn như:

CHỌN a1.user_id TỪ câu trả lời a1
WHERE a1.question_id = 1 VÀ a1.answer_value = 'Pooch'
THAM GIA THAM GIA trả lời a2 TRÊN a2.question_id = 2 
   VÀ a2.answer_value = 'Peach' VÀ a1.user_id = a2.user_id

Bảng a1 nên sử dụng chỉ mục đầu tiên. Tùy thuộc vào phân phối dữ liệu, trình tối ưu hóa có thể sử dụng một trong hai chỉ mục. Toàn bộ truy vấn phải được thỏa mãn từ các chỉ mục.


2

Một cách để tiếp cận nó là lấy một tập hợp con user_id và kiểm tra những thứ đó cho trận đấu thứ hai:

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

Sử dụng cấu trúc của 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');

Sản lượng:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.