【问题标题】:MySQL select users on multiple criteriaMySQL 根据多个条件选择用户
【发布时间】:2012-04-06 18:42:24
【问题描述】:

我的团队正在为一个学校项目开发一个 php/MySQL 网站。我有一个包含典型信息(ID、名字、姓氏等)的用户表。我还有一个问题表,其中包含如下示例数据。对于这个简化的示例,所有问题的答案都是数字。

表格问题:

qid | questionText
1   | 'favorite number'
2   | 'gpa'
3   | 'number of years doing ...'

等等

用户可以填写表格来回答任何或所有这些问题。注意:用户不需要回答所有问题,问题本身可能会在未来发生变化。

答案表如下所示:

表答案:

uid | qid | value
 37 |  1  |  42
 37 |  2  |  3.5
 38 |  2  |  3.6

等等。

现在,我正在处理该网站的搜索页面。我希望用户选择他们想要搜索的标准。我有一些工作,但我不确定它是否有效,或者它是否会扩展(并不是说这些表会很大——就像我说的,这是一个学校项目)。例如,我可能想列出所有喜欢的数字在 100 到 200 之间且 GPA 高于 2.0 的用户。目前,我有一个可以工作的查询生成器(据我所知,它创建了一个返回准确结果的有效查询)。此示例的查询生成器的结果如下所示:

SELECT u.ID, u.name (etc)
FROM User u
JOIN Answer a1 ON u.ID=a1.uid 
JOIN Answer a2 ON u.ID=a2.uid
WHERE 1
AND (a1.qid=1 AND a1.value>100 AND a1.value<200)
AND (a2.qid=2 AND a2.value>2.0)

我添加 WHERE 1 以便在 for 循环中添加“AND (...)”。我意识到我可以删除 '1' 并使用 implode(and,array) 并添加 where if 数组不为空,但我认为这是等价的。如果没有,我可以很容易地改变它。

如您所见,我为搜索者要求的每个条件添加了一个 JOIN。这也让我可以按 a1.value ASC 或 a2.value 等排序。

第一个问题: 这个餐桌组织至少有点像样吗?我们认为,由于问题的数量是可变的,而且并不是每个用户都回答了每个问题,所以这样的事情是必要的。

主要问题: 查询方式是否效率太低?我想将同一张桌子加入到自己身上多达十几次或两次(如果我们最终提出这么多问题的话)并不理想。我做了一些搜索,发现这两个帖子似乎有点触及我正在寻找的内容:

Mutiple criteria in 1 query

这在 EXISTS 中使用了多个嵌套(正确的术语?)查询

Search for products with multiple criteria

youssef azari 的其中一个 cmets 提到使用 'query 1' UNION 'query 2'

对于我正在尝试做的事情,这些中的任何一个会表现更好/更有意义吗?

额外问题:

为简单起见,我在上面省略了,但实际上我有 3 个表(用于数值问题、布尔值和文本) 决定使用单独的表是因为(据我所知)它要么就是这样,要么有一个大答案表,其中包含 3 个不同类型的值列,其中 2 个始终为空。

这适用于我当前的查询生成器 - 示例查询是

SELECT u.ID,...
FROM User u
JOIN AnswerBool b1 ON u.ID=b1.uid
JOIN AnswerNum n1 ON u.ID=n1.uid
JOIN AnswerText t1 ON u.ID=t1.uid 
WHERE 1
AND (b1.qid=1 AND b1.value=true)
AND (n1.qid=16 AND n1.value<999)
AND (t1.qid=23 AND t1.value LIKE '...')

考虑到这一点,获得结果的最佳方法是什么?

最后一段上下文: 我提到这是一个学校项目。虽然这是真的,但最终目标(这是一个本科生高级设计项目)是让一个部门使用我们的网站,让学生为他们的高级设计创建团队。对于规模的粗略估计,每个学期,该部门将有大约 200 名左右的学生使用我们的网站来组建团队。显然,当我们完成后,该部门将(希望)检查我们的站点是否存在安全问题和他们需要担心的其他问题(FERPA 和所有问题)。我们正在尝试考虑所有常见的安全实践和可扩展性问题,但最终,我们的代码可能会被其他人改进。

更新 根据 nnichols 的建议,我输入了大量数据并对不同的查询进行了一些测试。我在表中放置了大约 250 个用户,并且在 3 个表中的每一个中放置了大约 2000 个答案。我发现提供的链接非常有用

(链接已删除,因为我不能超链接超过两次)链接在 nnichols 的回复中

还有我发现的这个:

http://phpmaster.com/using-explain-to-write-better-mysql-queries/

我尝试了 3 种不同类型的查询,最后,我提出的那一种效果最好。

首先:使用 EXISTS

SELECT u.ID,...
FROM User u WHERE 1
AND EXISTS 
    (SELECT * FROM AnswerNumber 
    WHERE uid=u.ID AND qid=# AND value>#) -- or any condition on value
AND EXISTS
    (SELECT * FROM AnswerNumber
    WHERE uid=u.ID AND qid=another # AND some_condition(value))
AND EXISTS
    (SELECT * FROM AnswerText
...

我在 3 个答案表中的每一个上都使用了 10 个条件(导致 30 个 EXISTS)

第二:使用 IN - 一种非常相似的方法(甚至可能完全一样?),产生相同的结果

SELECT u.ID,...
FROM User u WHERE 1
AND (u.ID) IN (SELECT uid FROM AnswerNumber WHERE qid=# AND ...)
...

再次使用 30 个子查询。

我尝试的第三个与上面描述的相同(使用 30 个 JOIN)

对前两个使用EXPLAIN的结果如下:(相同)

表 u 上的主查询类型为 ALL(不好,尽管用户表并不大),搜索的行大约是用户表大小的两倍(不知道为什么)。 EXPLAIN 输出中的每一行都是对相关答案表的依赖查询,类型为 eq_ref (good),使用 WHERE 和 key=PRIMARY KEY 并且仅搜索 1 行。总体还不错。

对于我建议的查询(JOINing):

主要查询实际上是在您首先加入的任何表上(在我的情况下为 AnswerBoolean),其类型为 ref(优于 ALL)。搜索的行数等于任何人回答的问题数(因为任何人回答了 50 个不同的问题)(这将比用户数少得多)。对于 EXPLAIN 输出中的每个附加行,它是一个简单的查询,类型为 eq_ref (good),使用 WHERE 和 key=PRIMARY KEY 并且只搜索 1 行。总体几乎相同,但起始乘数较小。

JOIN 方法的最后一个优点:它是唯一一个我可以弄清楚如何按各种值(例如 n1.value)排序的方法。由于其他两个查询使用子查询,我无法访问特定子查询的值。添加 order by 子句确实将第一个查询中的额外字段更改为也具有“使用临时”(我相信,对于 order by 是必需的)和“使用文件排序”(不知道如何避免这种情况)。然而,即使有这些减速,行数仍然少得多,另外两个(据我所知)不能使用 order by。

【问题讨论】:

  • 我可能应该提到的一个细节:在所有答案表中,主键是 uid 和 qid。用户可以回答问题或更新问题的现有答案。特定用户的问题不会有多个条目。
  • 你的问题表是动态的还是固定的?换句话说,它会扩展还是以后会添加新问题?
  • 随着时间的推移会添加或删除新问题。
  • 哇,如果我知道将其称为(我对吗?)一个多通的关系,我可以更快地找到这个 - stackoverflow.com/questions/7364969/… @Erwin Brandstetter 的答案有大量信息,包括技术 4、5 和 6 - 这是我测试的三个!他甚至提到 4 和 5 是等价的。

标签: mysql


【解决方案1】:

您可以使用适当大的测试数据集并使用EXPLAIN 和/或the profiler 自己回答大部分问题。

您的 INNER JOIN 几乎肯定会比切换到 EXISTS 执行得更好,但这很容易使用合适的测试数据集和 EXPLAIN 进行测试。

【讨论】:

  • 感谢您的意见!我看过 EXPLAIN 但从来没有 SHOW PROFILE。我正在生成一些垃圾数据进行测试,然后我将直接进行测试。这种类型的搜索似乎相当普遍,所以我想知道是否有执行查询的标准方法(我几乎编造了我上面提供的那个,尽管我确信它已经完成了)。我将测试我可能会遇到的这些方法和其他方法的性能 - 我只是询问是否有针对此类查询的久经考验的最佳方法。
  • 我终于有时间进行一些测试 - 我会在问题中输入信息
  • 非常感谢您花时间发布您的发现。您的三个不同查询的查询时间是多少?您可能想尝试在值字段上使用索引,以查看是否可以取消文件排序,尽管使用这些小数据集它不应该招致太多的性能损失。我希望这被证明是一个有用的学习练习。
  • 我尝试向值字段添加索引,但文件排序仍然存在。没有任何查询(尽管它们很小)在有或没有索引的情况下也没有任何明显的性能差异。尽管这两个相似的查询搜索了 10 倍多的行,但它们都在大约 20 毫秒内完成,而 JOIN 在大约 40 毫秒内完成。我认为对于我们的网站,让 MySQL 进行排序将比在 php 中混合进一步查询和/或排序更方便,尽管时间略有增加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-20
  • 2012-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多