it-swarm.asia

Mencocokkan satu kolom dengan beberapa nilai tanpa tabel bergabung sendiri di MySQL

Kami memiliki tabel yang kami gunakan untuk menyimpan jawaban atas pertanyaan. Kami harus dapat menemukan pengguna yang memiliki jawaban tertentu untuk pertanyaan tertentu. Jadi, jika tabel kami terdiri dari data berikut:

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

dan kami ingin menemukan pengguna yang menjawab 'Pooch' untuk pertanyaan 1 dan 'Peach' untuk pertanyaan 2, SQL berikut ini (jelas) tidak akan berfungsi:

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

Pikiran pertama saya adalah bergabung sendiri di meja untuk setiap jawaban yang kami cari:

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'

Ini berfungsi, tetapi karena kami mengizinkan sejumlah filter pencarian sewenang-wenang, kami perlu menemukan sesuatu yang jauh lebih efisien. Solusi saya berikutnya adalah sesuatu seperti ini:

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

Namun, kami ingin pengguna dapat mengambil kuesioner yang sama dua kali, sehingga mereka berpotensi memiliki dua jawaban untuk pertanyaan 1 dalam tabel jawaban.

Jadi, sekarang saya bingung. Apa cara terbaik untuk mendekati ini? Terima kasih!

14

Kami bergabung dengan user_id dari tabel answers dalam rantai bergabung untuk mendapatkan data dari tabel lain, tetapi mengisolasi tabel jawaban SQL dan menulisnya dalam istilah sederhana seperti itu membantu saya menemukan solusinya:

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

Kami tidak perlu menggunakan sub-permintaan kedua.

5

Saya telah menemukan cara cerdas untuk melakukan kueri ini tanpa bergabung sendiri.

Saya menjalankan perintah ini di MySQL 5.5.8 untuk Windows dan mendapatkan hasil sebagai berikut:

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

Tampilan ini mengungkapkan bahwa John memberikan dua jawaban berbeda untuk pertanyaan 2 dan Sally memberikan dua jawaban berbeda untuk pertanyaan 1.

Untuk mengetahui pertanyaan mana yang dijawab berbeda oleh semua pengguna, cukup tempatkan kueri di atas dalam subquery dan periksa koma dalam daftar jawaban yang diberikan untuk mendapatkan hitungan jawaban berbeda sebagai berikut:

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;

Saya mengerti:

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

Sekarang cukup filter baris di mana multianswer_count = 1 menggunakan subquery lain:

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;

Inilah yang saya dapat:

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

Pada dasarnya, saya melakukan tiga pemindaian tabel: 1 di tabel utama, 2 di subqueries kecil. TIDAK BERGABUNG !!!

Cobalah !!!

8
RolandoMySQLDBA

Saya suka metode bergabung, sendiri:

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

Pembaruan Setelah pengujian dengan tabel yang lebih besar (~ 1 juta baris), metode ini memakan waktu lebih lama dari metode OR sederhana yang disebutkan dalam pertanyaan awal.

7
Derek Downey

Jika Anda memiliki satu set data yang besar, saya akan melakukan dua indeks:

  • question_id, answer_value, user_id; dan
  • user_id, question_id, answer_value.

Anda harus bergabung beberapa kali karena cara pengorganisasian data. Jika Anda tahu nilai pertanyaan mana yang paling tidak umum, Anda mungkin dapat sedikit mempercepat kueri, tetapi pengoptimal harus melakukannya untuk Anda.

Coba kueri sebagai:

SELECT a1.user_id DARI jawaban a1 
 WHERE a1.question_id = 1 AND a1.answer_value = 'Pooch' 
 INNER JOIN menjawab a2 ON a2.question_id = 2 
 DAN a2.answer_value = 'Persik' DAN a1.user_id = a2.user_id

Tabel a1 harus menggunakan indeks pertama. Bergantung pada distribusi data, pengoptimal dapat menggunakan indeks mana pun. Seluruh permintaan harus dipenuhi dari indeks.

4
BillThor

Salah satu cara untuk mendekatinya adalah dengan mendapatkan subset dari user_id dan menguji mereka untuk kecocokan kedua:

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

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

Hasil:

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)
2
randomx