1

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

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

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

我试图应用所有类型的连接、交叉点,但它总是以某种方式失败。有人可以帮我编写正确的查询,最好是轻量级/优化连接,因为数据库包含大量记录(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_idquestion_id表的索引option_idanswers

运行SQL Fiddle上给出的查询。

dnoeth 的答案的SQL Fiddle

用于 calcinai 答案的SQL Foddle 。

4

2 回答 2

1

使用 OR将所有过滤器添加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 语法。

于 2013-09-17T06:44:49.737 回答
1

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

这会产生您期望的结果。我假设搜索过滤器是根据数据库中的选项生成的,因此不要将实际值传递回查询,而是传递 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`);

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

于 2013-09-17T06:48:26.377 回答