编写此查询的更好/更有效的方法
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 可显着提高性能以上是关于编写此查询的更好/更有效的方法的主要内容,如果未能解决你的问题,请参考以下文章
有没有更有效的方法来编写 $('parent > child')?