MySQL交集

时间:2013-09-17 06:24:04

标签: mysql sql select intersection

我有一个现有网站,其数据库设计不正确且包含大量记录,因此我们无法更改数据库结构。

当前问题的数据库主要包含4个表,用户,问题,选项和答案。有一组标准的问题和选项,但对于每个用户,每组问题和选项的答案表中都有一行。数据库结构和示例数据可在SQL fiddle获得。

现在作为高级搜索的新要求,我需要通过应用多个搜索过滤器来查找用户。示例输入和预期输出在SQL Fiddle的注释中给出。

我试图应用所有类型的连接,交叉但总是以某种方式失败。有人可以帮我写正确的查询,最好是轻量级/优化连接,因为DB包含大量记录(10000多个用户,100多个问题,500多个选项和500000+记录在答案表中)?

编辑:基于两个答案,我使用了以下查询

SELECT u.id, u.first_name, u.last_name
FROM users u
    JOIN answers a ON a.user_id = u.id
WHERE (a.question_id = 1 AND a.option_id IN (3, 5))
    OR (a.question_id = 2 AND a.option_id IN (8))
GROUP BY u.id, u.first_name, u.last_name
HAVING
    SUM(CASE WHEN (a.question_id = 1 AND a.option_id IN (3, 5)) THEN 1 ELSE 0 END) >=1
    AND SUM(CASE WHEN (a.question_id = 2 AND a.option_id IN (8)) THEN 1 ELSE 0 END) >= 1;

请注意:在真实数据库中,user_id表的question_idoption_idanswers列已编入索引。

SQL Fiddle上运行查询。

SQL Fiddle代表dnoeth的答案。

SQL Foddle对于calcinai的回答。

2 个答案:

答案 0 :(得分:1)

使用OR将所有 n 过滤器添加到WHERE中并使用AND在HAVING(SUM(CASE))中重复它们:

SELECT u.id, u.first_name, u.last_name
FROM users u JOIN answers a
  ON a.user_id = u.id
JOIN questions q
  ON a.question_id = q.id
JOIN question_options o
  ON a.option_id = o.id
WHERE (q.question = 'Language known' AND o.OPTION IN ('French','Russian'))
   OR (q.question = 'height' AND o.OPTION = '1.51 - 1.7')
GROUP BY u.id, u.first_name, u.last_name
HAVING
  SUM(CASE WHEN (q.question = 'Language known' AND o.OPTION IN ('French','Russian')) THEN 1 ELSE 0 END) >=1
AND 
  SUM(CASE WHEN (q.question = 'height'         AND o.OPTION = '1.51 - 1.7')          THEN 1 ELSE 0 END) >= 1
;

我将您的联接更改为更易读的标准SQL语法。

答案 1 :(得分:1)

这需要对动态过滤器进行一些调整,但你真正想做的是按ID搜索,因为它意味着更少的连接和更快的查询。

这会产生您期望的结果。我假设搜索过滤器是基于数据库中的off选项生成的,因此不是将实际值传回给查询,而是传递ID。

多个内部联接将支持多个AND条件并自动减少结果集。

SELECT * FROM users u
INNER JOIN answers a ON a.user_id = u.id
  AND (a.question_id, a.option_id) IN ((1,3),(1,5)) # q 1: Lang, answer 3/5: En/Ru
INNER JOIN answers a2 ON a2.user_id = u.id
  AND (a2.question_id, a2.option_id) = (2,8) # q 2: Height, answer 8: 1.71...
GROUP BY u.id;

我建议确保搜索的用户名为(user_id,question_id,option_id):

ALTER TABLE `answers` ADD INDEX idx_search(`user_id`, `question_id`, `option_id`);

否则它应该使用连接的主键(如果正确定义),所以它会很快。