【问题标题】:MySQL Join Optimisation: Improving join type with derived tables and GROUP BYMySQL 连接优化:使用派生表和 GROUP BY 改进连接类型
【发布时间】:2011-09-19 09:44:57
【问题描述】:

我正在尝试改进执行以下操作的查询:

对于每项工作,将所有成本相加,将发票金额相加,然后计算损益。成本来自几个不同的表,例如采购订单、用户事件(工程师分配的时间/他在现场花费的时间)、使用的库存等。
该查询还需要输出一些其他列,例如作品的站点名称,以便该列可以排序(在所有这些之后附加一个 ORDER BY)。

SELECT
    jobs.job_id,
    jobs.start_date,
    jobs.end_date,
    events.time,
    sites.name site,
    IFNULL(stock_cost,0) stock_cost,
    labour,
    materials,
    labour+materials+plant+expenses revenue,
    (labour+materials+plant)-(time*3557/360000+IFNULL(orders_cost,0)+IFNULL(stock_cost,0)) profit,
    ((labour+materials+plant)-(time*3557/360000+IFNULL(orders_cost,0)+IFNULL(stock_cost,0)))/(time*3557/360000+IFNULL(orders_cost,0)+IFNULL(stock_cost,0)) ratio

FROM
    jobs

    LEFT JOIN (
        SELECT
            job_id,
            SUM(labour_charge) labour,
            SUM(materials_charge) materials,
            SUM(plant_hire_charge) plant,
            SUM(expenses) expenses
        FROM invoices
        GROUP BY job_id
        ORDER BY NULL
    ) invoices USING(job_id)

    LEFT JOIN (
        SELECT
            job_id,
            SUM(IF(start_onsite && end_onsite,end_onsite-start_onsite,end-start)) time,
            SUM(travel+parking+materials) user_expenses
        FROM users_events
        WHERE type='job'
        GROUP BY job_id
        ORDER BY NULL
    ) events USING(job_id)

    LEFT JOIN (
        SELECT
            job_id,
            SUM(IFNULL(total,0))*0.01 orders_cost
        FROM purchaseorders
        GROUP BY job_id
        ORDER BY NULL
    ) purchaseorders USING(job_id)

    LEFT JOIN (
        SELECT
            location job_id,
            SUM(amount*cost))*0.01 stock_cost
        FROM stock_location
        LEFT JOIN stock_items ON stock_items.id=stock_location.stock_id
        WHERE location>=3000 AND amount>0 AND cost>0
        GROUP BY location
        ORDER BY NULL
    ) stock USING(job_id)

    LEFT JOIN contacts_sites sites ON sites.id=jobs.site_id;

我读到了这个:http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html,但看不到我如何/是否可以在其中应用任何东西。 出于测试目的,我尝试在左侧、右侧和中心字段上添加各种索引,但对 EXPLAIN 输出没有任何改进:

+----+-------------+----------------+--------+------------------------+---------+---------+------------------------------------+-------+-------------------------------+
| id | select_type | table          | type   | possible_keys          | key     | key_len | ref                                | rows  | Extra                         |
+----+-------------+----------------+--------+------------------------+---------+---------+------------------------------------+-------+-------------------------------+
|  1 | PRIMARY     | jobs           | ALL    | NULL                   | NULL    | NULL    | NULL                               |  7088 |                               |
|  1 | PRIMARY     | <derived2>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |  5038 |                               |
|  1 | PRIMARY     | <derived3>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |  6476 |                               |
|  1 | PRIMARY     | <derived4>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |   904 |                               |
|  1 | PRIMARY     | <derived5>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |   531 |                               |
|  1 | PRIMARY     | sites          | eq_ref | PRIMARY                | PRIMARY | 4       | bestbee_db.jobs.site_id            |     1 |                               |
|  5 | DERIVED     | stock_location | ALL    | stock,location,amount,…| NULL    | NULL    | NULL                               |  5426 | Using where; Using temporary; |
|  5 | DERIVED     | stock_items    | eq_ref | PRIMARY                | PRIMARY | 4       | bestbee_db.stock_location.stock_id |     1 | Using where                   |
|  4 | DERIVED     | purchaseorders | ALL    | NULL                   | NULL    | NULL    | NULL                               |  1445 | Using temporary;              |
|  3 | DERIVED     | users_events   | ALL    | type,type_job          | NULL    | NULL    | NULL                               | 11295 | Using where; Using temporary; |
|  2 | DERIVED     | invoices       | ALL    | NULL                   | NULL    | NULL    | NULL                               |  5320 | Using temporary;              |
+----+-------------+----------------+--------+------------------------+---------+---------+------------------------------------+-------+-------------------------------+

生成的行数为 5 x 10^21(低于我开始优化此查询之前的 3 x 10^42!)
目前执行需要 7 秒(从 26 秒减少),但我希望不到 1 秒。

顺便说一句:GROUP BY x ORDER BY NULL 是消除子查询中不必要的文件排序的好方法! (来自http://www.mysqlperformanceblog.com/2006/09/04/group_concat-useful-group-by-extension/

【问题讨论】:

  • @devart 这已经尝试过了,并且对索引的使用没有任何影响。我刚刚注意到 EXPLAIN 输出来自我添加 ORDER BY NULL 之前(我在写问题的中途做了)。对此感到抱歉。我将用发布的查询中的一个替换它(为简洁起见,再次截断可能的键)
  • 您是从 Jobs.. 开始的,这是所有工作,即使是那些没有发票或费用而不会导致利润或损失的工作(除了项目的研究时间和准备投标的时间) /合同报价)。或者...您是否只希望那些至少有一些发票的工作...如果是这样,可以进行一些小的调整来调整查询以优化它。
  • @drapp 事实上,我以前在网站、事件和发票表中使用 INNER JOIN,假设我的老板只想查看我可以可靠计算损益的工作(有已开具发票),每个工作实际上都分配了一名工程师来完成工作,并且每个 jobs.site_id 将是一个有效的外键值。 MySQL 确实使用了我创建的一些索引,但我的老板还想查看到目前为止所有正在进行的工作所产生的成本,无论是否分配了任何工程师时间,我只剩下 LEFT JOINS。跨度>

标签: mysql group-by query-optimization derived-table filesort


【解决方案1】:

根据您对我的问题的评论,我将执行以下操作...

在最顶端...

SELECT STRAIGHT_JOIN(只需添加“STRAIGH_JOIN”关键字)

然后,对于发票、事件、p/o 等的每个子查询,将 ORDER BY 显式更改为 JOB_ID,这样它可能有助于针对主 JOBS 表连接进行优化。

最后,确保您的每个子查询表在 Job_ID(Invoices、User_events、PurchaseOrders、Stock_Location)上都有一个索引

此外,对于 Stock_Location 表,您可能希望通过在上添加复合索引来帮助子查询的 WHERE 子句
(job_id, location, amount) 三个字段深度应该足够了,即使你有键加上 3 个 where 条件元素。

【讨论】:

  • 对于 stock_location,location 列如果其值 >= 3000 则为 job_id,否则为仓库 (0) 或 van_id (1-2999)。我可以忽略后两种位置的库存。
  • 现在不会早于星期一我再次开始处理这段代码,所以届时将投票/接受。
  • 好的,星期一已经是一年多以前了,我还没有回到那段代码来测试你的解决方案。我保证有一天会实现它:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-10
  • 2019-01-15
  • 2013-01-08
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多