MySQL에서 자체 조인 테이블없이 여러 값에 대해 단일 열 일치


14

질문에 대한 답변을 저장하는 데 사용하는 테이블이 있습니다. 특정 질문에 대한 답변이있는 사용자를 찾을 수 있어야합니다. 따라서 테이블이 다음 데이터로 구성된 경우 :

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

질문 1에 대해 'Pooch', 질문 2에 대해 'Peach'라고 대답하는 사용자를 찾으려면 다음 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 번 질문에 대한 답변이 2 개있을 수 있습니다.

그래서 지금은졌습니다. 이것에 접근하는 가장 좋은 방법은 무엇입니까? 감사!

답변:


8

자체 조인 없이이 쿼리를 수행하는 영리한 방법을 찾았습니다.

Windows 용 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
나는 당신이 당신의 답변에 노력의 수준을 항상 감사합니다.
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

업데이트 더 큰 테이블 (~ 100 만 행)로 테스트 한 후이 방법은 OR원래 질문에서 언급 한 간단한 방법 보다 훨씬 오래 걸렸습니다 .


답장을 보내 주셔서 감사합니다. 문제는 이것이 잠재적으로 큰 테이블 일 수 있으며 5-6 번 조인해야한다는 것은 큰 성능 저하를 의미 할 수 있다는 것입니다.
Christopher Armstrong

좋은 질문. 내가 알지 못하는 것처럼 테스트하기 위해 테스트 케이스를 작성하고 있습니다. 완료되면 결과를 게시 할 것입니다
Derek Downey

1
그래서 임의의 사용자, 질문 / 답변 쌍으로 백만 개의 행을 삽입했습니다. 조인은 여전히 ​​557 초이며 OR 쿼리는 1.84 초 안에 완료되었습니다 ... 지금 코너에 앉아 있습니다.
데릭 다우니

테스트 테이블에 인덱스가 있습니까? 백만 행 테이블을 몇 번 스캔하는 경우 조금 느릴 것입니다.
마리안

@Marian 예, 카디널리티가 매우 낮으므로 (question_id, answer_value) 문제에 대한 색인을 추가했기 때문에 많은 도움이되지 않습니다 (각 조인은 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입니다.

데이터가 구성되는 방식으로 인해 여러 번 참여해야합니다. 어떤 질문에 대한 가장 일반적인 값을 알고 있다면 쿼리 속도를 약간 높일 수 있지만 최적화 프로그램이 대신해야합니다.

다음과 같이 쿼리를 시도하십시오.

SELECT a1.user_id FROM이 a1로 응답
a1.question_id = 1 및 a1.answer_value = '푸치'
내부 가입은 a2 ON a2.question_id = 2로 답변 
   AND a2.answer_value = '복숭아'AND 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');

롤란도의 구조를 사용하여 :

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.