“ in 子句”中的 MySQL 项数

我有三个表来定义用户:

USER: user_id (int), username (varchar)
USER_METADATA_FIELD: user_metadata_field_id (int), field_name (varchar)
USER_METADATA: user_metadata_field_id (int), user_id (int), field_value (varchar)

我希望创建一个中间层用户,该用户可以访问应用程序中的其他用户。为了确定登录的用户可以访问哪些用户,我使用了如下子查询:

SELECT user_id FROM user WHERE user_id
IN (SELECT user_id
FROM user_metadata
WHERE user_metadata_field_id = 1 AND field_value = 'foo')

目前,我将子查询字符串存储在一个变量中,然后在每次需要拉出用户列表时将其动态插入到外部查询中。这样做之后,我想,“只存储一个实际的 user_id字符串肯定会更好”。

所以不是把它存储在一个变量里。

$subSql = "SELECT user_id FROM user_metadata WHERE user_metadata_field_id = 1 AND field_value = 'foo'";

... 我实际上执行查询并像这样存储结果..。

$subSql = "12, 56, 89, 100, 1234, 890";

然后,当我需要拉出登录用户可以访问的用户列表时,我可以这样做:

$sql = "SELECT user_id FROM user WHERE user_id IN ($subSql)";

最后的问题是:

您可以在 MySQLIN子句中使用多少项?为了每次执行外部查询,存储实际 id 而不是 subsql 语句必须更快,对吗?

79688 次浏览

从一定数量开始,IN表更快。

MySQL在其代码中有一些内容,使得在大量常量值上构建范围比在嵌套循环中执行相同的操作要慢。

有关性能细节,请参阅我的博客中的这篇文章:

正如 Quassnoi 的回应所暗示的,一个 在达到任何可能的极限之前,偶然发现了其他的实际考虑是由某个 MySql 版本的实现(*)强加的。因此,随着管理员用户数量(或者其他可能需要 IN 结构的条件)的增长,应该寻求使用文字“ IN”的替代方法,比如使用临时(甚至是永久)表。

由于您正在考虑对“管理用户”标准的特殊处理,出于性能目的,我想提供一个评论和建议。

评论: 这是不是一个过早优化的例子?
我不知道这个数据库的具体情况,它的体积,复杂性等。是的,我知道 EAV (实体-属性-价值)格式的一些性能贡献,但是我认为即使对于成功的企业来说,帐户数据库也很少超过10,000个用户。因此,即使每个用户有很多属性,我们仍然在寻找一个相对较小的 EAV 表,它可能不需要这种类型的优化。(另一方面,一些其他的优化技巧可能在其他领域受到欢迎)。此外,典型的用例,相对于其他查询,涉及到对帐户数据库的相对较少的查询,因此这也是为了应用程序的帐户相关特性而推迟任何重要的性能考虑的另一个原因。译注:

建议: 也许可以使用“重新规范化的属性”
对于单值属性,特别是如果它们很短,可以在 Entity 表(本例中为‘ USER’表)中移动(或复制)它们。这在插入或更新项时引入了一些逻辑,但是这与许多连接(或子查询)相同,并且还提供了考虑多字段索引以支持最常见用例的机会。

(*)有限制吗?
我从来没有读到过这样的限制; 我知道 Oracle 曾经有过1000个限制,而 MSSQL 没有; 当然,所有的服务器都有一个基于 SQL 语句总长度的限制,但这是一个非常大的数字!如果一个人偶然发现了这一点,他/她有其他的问题... ; -)

MySQL 的 IN 子句本身没有这样的限制。我尝试了8000元素它的工作对我来说很好。堆栈溢出错误可以声明为变量,

来自 手动操作:

IN列表中的值的数量仅受 max_allowed_packet值的限制。

如果在 IN()子句中有超过1000个值,MariaDB 似乎会自动创建临时表以提高性能。您可以使用 EXPLAIN看到这一点。