在 MYSQL 中针对带有子查询的查询进行查询优化?

Query optimization in MYSQL for a query with sub queries?

在一次作业中,有人给了我一个查询并要求我对其进行优化。 查询是:

SELECT
    C.yearID as year,
    name as teamName,
    C.lgID as league,
    D.cnt as totalBatters,
    C.cnt as aboveAverageBatters
FROM
    (SELECT 
        count(masterID) as cnt, A.yearID, A.teamID, A.lgID
    FROM
        (select 
        masterID,
            teamID,
            yearID,
            lgID,
            sum(AB),
            sum(H),
            sum(H) / sum(AB) as avg
    FROM
        batting
    GROUP BY teamID , yearID , lgID , masterID) B, (select 
        teamID,
            yearID,
            lgID,
            sum(AB),
            sum(H),
            sum(H) / sum(AB) as avg
    FROM
        batting
    WHERE ab is not null
    GROUP BY teamID , yearID , lgID) A
    WHERE
        A.avg >= B.avg AND A.teamID = B.teamID
            AND A.yearID = B.yearID
            AND A.lgID = B.lgID
    GROUP BY teamID , yearID , lgID) C,
    (SELECT 
        count(masterID) as cnt, yearID, teamID, lgID
    FROM
        batting
    WHERE ab is not null
    GROUP BY yearID , teamID , lgID) D, 
    teams
WHERE
    C.cnt / D.cnt >= 0.75
        AND C.yearID = D.yearID
        AND C.teamID = D.teamID
        AND C.lgID = D.lgID
        AND teams.yearID = C.yearID
        AND teams.lgID = C.lgID
        AND teams.teamID = C.teamID

我想知道可以做些什么来优化它?我是这个概念的新手,对如何进行有点困惑。一般来说,如何优化其中包含 select 语句的子查询?

In General, how to optimize sub queries which have select statements in it?

这里有一些帮助您入门的想法。我会尊重它是一项作业这一事实,最后,您将通过自己完成并一路学习,更好地理解 SQL 查询。

我希望作业包括一种数据集,您可以将其导入 MySQL 以便您可以 运行 在进行更改时进行查询并注意对执行计划的影响和整体表现。


别名

在考虑优化之前,或许您可以看看如何使代码更易于阅读、理解和维护。子查询的行为方式类似于常规表,因此,它们应该被赋予 aliases/names ,这对于数据集 意味着 .

是有意义的

它们的别名是 BACD,它们看起来几乎是故意命名的,以混淆视听,但实际上你会惊讶于你看到它们的频率现实生活中的可怜 naming/aliasing,生产 SQL 代码。

尝试单独查看(如果可以,运行ning)每个子查询,查看字段及其含义,然后用一个好的名称替换别名,并更新相应地不同的列。这将优化查询以获得更好的清晰度,并最终提高可维护性。


JOINs

希望在完成此作业时,已经涵盖了各种类型的 JOIN 操作。如果没有,here is a good summary from a Whosebug answer. There is a large number of other resources covering the ins and outs of JOIN, including a good post on TechOnTheNet.

让我们剥离子查询,看看整个查询的结构。我用注释替换了逻辑以使其更明显:

SELECT
--columns
FROM 
(
    SELECT  
    --columns
    FROM 
    (
        select 
        --columns
        FROM batting
    ) B, 
    (
        select 
        --columns
    ) A
    WHERE
    --some comparisons of averages
) C,
(
    SELECT 
     --columns
    FROM batting
) D, 
teams
WHERE
    --a filter based on a calculation
    C.cnt / D.cnt >= 0.75
    --um... what is all this stuff doing down here?
    --shouldn't those be in a JOIN?
        AND C.yearID = D.yearID
        AND C.teamID = D.teamID
        AND C.lgID = D.lgID
        AND teams.yearID = C.yearID
        AND teams.lgID = C.lgID
        AND teams.teamID = C.teamID

你有没有注意到任何奇怪的东西或任何看起来很奇怪的东西?如果您以前没有读过这篇文章,我强烈建议您阅读 Bad habits to kick : using old-style JOINs by Aaron Bertrand

看完之后,再看看这个查询的骨架,你可以通过使用现代 JOIN 做出的改进应该很突出。这将使查询在清晰度和可维护性方面更加优化。


关键字大小写的一致性

另一种提高可读性的方法是使用一致的关键字大小写。实际上,使用 CAPITAL CASEsmall case 大约是 50/50。对于一个脚本来说这可能看起来微不足道,但是当这种不一致分布在整个代码库中时,对于下一个必须在其中开发和维护它的人来说真的很烦人。


性能

所以,到现在为止,如果您已经应用了所有的东西,代码应该更容易破译了。就性能而言,有两件事对我来说是有害的。有很多聚合,因此有很多 GROUP BYs.

首先单独查看每个子查询,然后查看每个聚合。查看每个字段在整个查询的上下文中是如何使用的。查看哪些您可以删除,也许编写查询的人最初认为他们需要,但最终没有使用并忘记删除它们。

GROUP BY 字段尝试相同的策略,这些字段是您未聚合但包含在具有一个或多个聚合操作的查询中的每个字段。 GROUP BY 可能会变得非常昂贵,而且派生子查询也有 GROUP BY.

的事实使情况更加复杂。

您可以尝试一些其他技巧,这些技巧更高级并且可以在 I/O 的折衷下提高执行力,例如将一个或多个子查询的结果集提取到临时表中,这将释放锁定主表。

像这样的优化可能不一定总能提高执行 速度 本身,但在数据库服务器处于负载下的生产环境中,速度通常不是优化的主要关注点,但“轻便”(或尽可能小的服务器负载足迹)通常比最终使用更多资源的原始速度更有价值。


希望对您有所帮助!