it-swarm-eu.dev

Přizpůsobení jednoho sloupce proti více hodnotám bez samostatné tabulky v MySQL

Máme tabulku, pomocí které ukládáme odpovědi na otázky. Musíme být schopni najít uživatele, kteří mají určité odpovědi na konkrétní otázky. Pokud tedy naše tabulka obsahuje následující údaje:

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

a chceme najít uživatele, kteří odpovídají na otázku 1 „Pooch“ a na otázku 2 „Broskev“, následující SQL (samozřejmě) nebude worK:

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

Moje první myšlenka byla, že se připojím ke stolu pro každou odpověď, kterou hledáme:

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'

Funguje to, ale protože povolujeme libovolný počet vyhledávacích filtrů, musíme najít něco mnohem účinnějšího. Moje další řešení bylo něco jako toto:

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

Chceme však, aby uživatelé mohli vzít stejný dotazník dvakrát, takže by potenciálně mohli mít dvě odpovědi na otázku 1 v tabulce odpovědí.

Teď jsem v rozpacích. Jaký je nejlepší způsob, jak toho dosáhnout? Dík!

14

Připojili jsme se k user_id z tabulky answers v řetězci spojení, abych získal data z jiných tabulek, ale izolace tabulky odpovědí SQL a její psaní jednoduchými slovy mi pomohlo najít řešení:

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

Zbytečně jsme použili druhý dílčí dotaz.

5

Našel jsem chytrý způsob, jak udělat tento dotaz bez vlastního připojení.

Tyto příkazy jsem spustil v MySQL 5.5.8 pro Windows a získal jsem následující výsledky:

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

Toto zobrazení ukazuje, že John dal na otázku 2 dvě různé odpovědi a Sally na otázku 1 odpověděla dvěma různými odpověďmi.

Chcete-li zachytit, na které dotazy všichni uživatelé odpověděli odlišně, stačí umístit výše uvedený dotaz do poddotazu a zkontrolovat čárku v seznamu daných odpovědí, abyste získali počet různých odpovědí takto:

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;

Mám to:

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

Nyní jen odfiltrujte řádky, kde multianswer_count = 1, pomocí jiného poddotazu:

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;

To je to, co jsem dostal:

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

V podstatě jsem provedl tři skenování tabulky: 1 na hlavní tabulce, 2 na malých poddotazech. ŽÁDNÉ PŘIPOJENÍ !!!

Pokusit se !!!

8
RolandoMySQLDBA

Líbí se mi metoda připojení, sám:

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

Aktualizace Po testování s větší tabulkou (~ 1 milion řádků) trvalo tato metoda podstatně déle než jednoduchá metoda OR uvedená v původní otázce.

7
Derek Downey

Pokud máte velkou sadu dat, udělal bych dva indexy:

  • query_id, answer_value, user_id; a
  • user_id, question_id, answer_value.

Budete se muset připojit vícekrát kvůli způsobu organizace dat. Pokud víte, která hodnota pro kterou otázku je nejméně běžná, můžete dotaz trochu urychlit, ale optimalizátor by to měl udělat za vás.

Zkuste dotaz jako:

VYBERTE a1.user_id Z odpovědí a1 
 KDE a1.question_id = 1 AND a1.answer_value = 'Pooch' 
 VNITŘNÍ JOIN odpovědi a2 ON a2.question_id = 2 
 A a2.answer_value = 'Peach' AND a1.user_id = a2.user_id

Tabulka a1 by měla používat první index. V závislosti na distribuci dat může optimalizátor použít buď index. Celý dotaz by měl být uspokojen z indexů.

4
BillThor

Jedním ze způsobů, jak se k němu dostat, je získat podmnožinu user_id a otestovat je pro druhý zápas:

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

Použití Rolandovy struktury:

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

Výnosy:

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