编写此查询的更好/更有效的方法

Posted

技术标签:

【中文标题】编写此查询的更好/更有效的方法【英文标题】:A better / more efficient way to write this query 【发布时间】:2013-12-17 20:00:19 【问题描述】:

我正在尝试计算表中的记录数。 该表称为 affiliations,只有 4 列(其中 2 列是外键)

我想统计附属列为0且business_id与特定account_email相关的记录数。

我知道如何使用 IN 关键字执行此查询,但我想知道是否有更好或更有效的方法来执行此操作。

这是查询的 IN 版本:

SELECT COUNT(1) FROM affiliations 
WHERE business_id IN (
    SELECT business_id 
    FROM affiliations 
    WHERE account_email = 'address@domain.ext'
) AND affiliated = 0

我知道我可以用 EXISTS 替换它:

SELECT COUNT(1) FROM affiliations 
WHERE EXISTS (
    SELECT 1 FROM affiliations 
    WHERE account_email = 'address@domain.ext'
) AND affiliated = 0

带有 EXISTS 的语句会起作用吗?如前所述,有没有更好的方法来做到这一点?

提前致谢!

【问题讨论】:

一些附加信息:business_id 在此表中不是唯一的。数据库中账户和企业之间存在多对多关系,该表包含相关的account_emails和business_ids。我正在选择一个 account_email,并想获取该帐户相关的所有 business_id,然后计算也与这些业务中的每一个相关的帐户数 请发布此查询的 EXPLAIN ANALYZE 输出 如何计算 account_email = 'address@domain.ext' 和附属 = 0 的 business_id 这只会给我与“address@domain.ext”相关联的企业数量为 0。我想获得与“相同企业”相关的帐户数量address@domain.ext' 附属为 0 @Ben Guest:那么,有没有机会根据要求看到EXPLAIN 【参考方案1】:

我会使用存在,但也要记住将子查询与主表相关联,如下所示。

SELECT COUNT(1) FROM affiliations a
WHERE exists (
    SELECT 1
    FROM affiliations a1
    WHERE account_email = 'address@domain.ext'
    and a1.business_id=a.business_id
) AND affiliated = 0

【讨论】:

【参考方案2】:

带有IN 子句的问题的第一个查询不等于带有EXIST 的第二个查询。 要使用IN 转换第一个查询,您必须使用依赖子查询:

SELECT COUNT(1) FROM affiliations a1
WHERE EXISTS (
    SELECT 1 FROM affiliations a2
    WHERE account_email = 'address@domain.ext'
      AND a1.business_id = a2.business_id
) AND affiliated = 0

注意这个条件:AND a1.business_id = a2.business_id 上述查询在语义上等同于您使用 IN 进行的第一个查询。 它们的性能也是一样的,因为 mysql 在优化阶段会在内部转换这种形式的条件:outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)

进入这个:

EXISTS (SELECT 1 FROM ... WHERE subquery_where AND outer_expr=inner_expr)

查看此链接了解详情:http://dev.mysql.com/doc/refman/5.0/en/subquery-optimization-with-exists.html 请特别注意有关 NULL 值以及 NULL 如何影响优化器的讨论。 简而言之 - 如果 business_id 列被声明为 NOT NULL,那么 MySql 能够优化这两个查询。 查看最终结论(在此链接的页面底部):

为帮助查询优化器更好地执行您的查询,请使用以下提示:

如果确实是,则必须将列声明为 NOT NULL。 (这也有助于优化器的其他方面。)

如果您不需要区分 NULL 和 FALSE 子查询结果,您可以轻松避免执行缓慢的路径。替换如下所示的比较:

outer_expr IN (SELECT inner_expr FROM ...)

用这个表达式:

(outer_expr IS NOT NULL) AND (outer_expr IN (SELECT inner_expr FROM ...))

然后 NULL IN (SELECT ...) 将永远不会被评估,因为一旦表达式结果明确,MySQL 就会停止评估 AND 部分。

【讨论】:

【参考方案3】:

使用 JOIN 而不是 IN。如果您尝试匹配很多值,IN 的性能会很糟糕

SELECT COUNT(1) 
FROM affiliations AS ABB2
    JOIN (SELECT business_id 
          FROM affiliations 
          WHERE account_email = 'address@domain.ext') AS ABB1
        ON ABB1.business_id = ABB2.business_id
WHERE affiliated = 0

【讨论】:

@Lloyd,在语义上不等同于 SEMI JON(存在/存在)的 JOIN,请参阅此演示:sqlfiddle.com/#!2/b228c/1 注意您的查询结果和来自答案的查询。此外,当 IN 包含大量值(常量)时,IN 的性能可能会很差,但这不适用于 IN ( subquery ) 的查询,MySql 能够优化这样的查询,并且可能更快那个紧张的人。 好的,谢谢您的信息。我可能会坚持使用 IN 语句。我猜 IN 关键字的存在是有原因的,这似乎是一个很好的使用案例。 @kordirko 我假设 business_id 是唯一的。如果没有,您可以在子查询中添加 GROUP BY,您将获得相同的结果。我知道一些极端情况,其中 IN 可能比直接 JOIN 稍快,但在绝大多数情况下,JOIN 是相同的,如果不比 IN 快的话。在旧版本的 MySQL 或未运行 InnoDB 的情况下尤其如此。 @BenGuest 我建议对您的数据集运行这两个查询。根据表上的索引和您使用的 MySQL 版本,您可以看到使用 JOIN 可显着提高性能

以上是关于编写此查询的更好/更有效的方法的主要内容,如果未能解决你的问题,请参考以下文章

有没有更有效的方法来执行这个嵌套的 SQL 查询?

编写此查询的更好方法

编写以下 SQL 查询的更有效方法

有没有更有效的方法来编写 $('parent > child')?

子选择或连接?有没有更好的方法来编写这个 mysql 查询?

有没有更有效/可读/更漂亮的方式来编写此条件?