74

我有三个表来定义用户:

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的 s”。

因此,与其将其存储在变量中...

$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)";

最后是问题:

IN你可以在 MySQL CLAUSE中使用多少项?每次执行外部查询时,存储实际 id 而不是 sub-sql 语句必须更快,对吧?

4

5 回答 5

161

手册

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

于 2012-01-10T19:02:08.577 回答
36

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

MySQL它的代码中有一些东西,这使得在大量常量值上构建一个范围比在嵌套循环中做同样的事情要慢。

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

于 2009-10-07T15:42:50.423 回答
11

正如 Quassnoi 的回应中所暗示的,达到给定 MySql 版本的实现 (*) 所施加的任何可能限制之前,人们会偶然发现其他实际考虑因素。因此,随着管理员用户(或可能需要 IN 构造的其他标准)数量的增长,人们应该寻求使用文字“IN”的替代方案,例如使用临时(甚至永久)表。

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

评论:这可能是过早优化的情况吗?
我不知道这个数据库的细节、它的容量、复杂性等。而且,是的,我知道要向 EAV(实体-属性-值)格式致敬,但我在想即使对于成功的企业,帐户数据库的用户数也很少超过 10,000。因此,即使每个用户有很多属性,我们仍然在查看一个相对较小的 EAV 表,这可能不需要这种类型的优化。(另一方面,其他一些优化技巧可能会在其他领域受到欢迎)。
此外,相对于其他查询,典型用例涉及对帐户数据库的相对较少的查询,因此这是推迟对应用程序的帐户相关功能进行任何重要性能考虑的另一个原因。

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

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

于 2009-10-07T16:16:04.533 回答
7

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

于 2011-04-20T07:31:37.587 回答
0

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

于 2020-01-30T10:34:55.600 回答