我需要优化mysql选择查询或使其更快

时间:2019-03-28 21:08:06

标签: mysql sql query-optimization

我有一个选择查询,该查询一次从mysql 5.5数据库中选择了超过5万条记录,并且这个数量有望增长。该查询包含多个子查询,这些子查询要花120多秒才能执行。

最初,sale_itemsstock中的某些表没有ID密钥,因此我添加了更多

SELECT
  `p`.`id` AS `id`,
  `p`.`Name` AS `Name`,
  `p`.`Created` AS `Created`,
  `p`.`Image` AS `Image`,
  `s`.`company` AS `supplier`,
  `s`.`ID` AS `supplier_id`,
  `c`.`name` AS `category`,
  IFNULL((SELECT
    SUM(`stocks`.`Total_Quantity`)
  FROM `stocks`
  WHERE (`stocks`.`Product_ID` = `p`.`id`)), 0) AS `total_qty`,
  IFNULL((SELECT
    SUM(`sale_items`.`quantity`)
  FROM `sale_items`
  WHERE (`sale_items`.`product_id` = `p`.`id`)), 0) AS `total_sold`,
  IFNULL((SELECT
    SUM(`sale_items`.`quantity`)
  FROM `sale_items`
  WHERE ((`sale_items`.`product_id` = `p`.`id`) AND `sale_items`.`Sale_ID` IN (SELECT
    `refunds`.`Sale_ID`
  FROM `refunds`))), 0) AS `total_refund`
  FROM ((`products` `p`
    LEFT JOIN `cats` `c`
      ON ((`c`.`ID` = `p`.`cat_id`)))
    LEFT JOIN `suppliers` `s`
      ON ((`s`.`ID` = `p`.`supplier_id`)))

这是解释结果

+----+--------------------+------------+----------------+------------------------+------------------------+---------+---------------------------------
| id | select_type        | table      | type           | possible_keys          | key                    | key_len | ref     | rows  | Extra                    |
+----+--------------------+------------+----------------+------------------------+------------------------+---------+---------------------------------
|  1 | PRIMARY            | <derived2> | ALL            | NULL                   | NULL                   | NULL    | NULL      | 20981 |                          |
|  2 | DERIVED            | p          | ALL            | NULL                   | NULL                   | NULL    | NULL      | 20934 |                          |
|  2 | DERIVED            | c          | eq_ref         | PRIMARY                | PRIMARY                | 4       | p.cat_id      |     1 |                          |
|  2 | DERIVED            | s          | eq_ref         | PRIMARY                | PRIMARY                | 4       | p.supplier_id |     1 |                          |
|  5 | DEPENDENT SUBQUERY | sale_items  | ref            | sales_items_product_id | sales_items_product_id | 5       | p.id   |    33 | Using where              |
|  6 | DEPENDENT SUBQUERY | refunds    | index_subquery | IDX_refunds_sale_id    | IDX_refunds_sale_id    | 5       | func    |     1 | Using index; Using where |
|  4 | DEPENDENT SUBQUERY | sale_items  | ref            | sales_items_product_id | sales_items_product_id | 5       | p.id   |    33 | Using where              |
|  3 | DEPENDENT SUBQUERY | stocks     | ref            | IDX_stocks_product_id  | IDX_stocks_product_id  | 5       | p.id    |     1 | Using where              |
+----+--------------------+------------+----------------+------------------------+------------------------+---------+---------------------------------

我希望查询最多花费不到3秒的时间,但是我似乎无法找出优化此查询的最佳方法

1 个答案:

答案 0 :(得分:2)

查询对我来说很好。您选择所有数据并汇总其中一些数据。这需要时间。您的解释计划显示ID上有索引,这很好。乍一看,我们在这里似乎无能为力...

但是,

您可以 做的是提供覆盖索引,即包含表中需要的所有列的索引,因此可以直接从索引中获取数据。

group = c(0,0,1,1,1,0,0,2,2,2,2,2,2,0,0,0,0,0,0)
data.frame(df, group)

这确实可以增强您的查询。

您可以尝试的关于查询本身的方法是将子查询移至create index idx1 on cats(id, name); create index idx2 on suppliers(id, company); create index idx3 on stocks(product_id, total_quantity); create index idx4 on sale_items(product_id, quantity, sale_id); 子句。 MySQL的优化器不是很好,因此尽管它应该获得相同的执行计划,但很可能是因为它偏爱FROM子句。

FROM

(如果SELECT p.id, p.name, p.created, p.image, s.company as supplier, s.id AS supplier_id, c.name AS category, COALESCE(st.total, 0) AS total_qty, COALESCE(si.total, 0) AS total_sold, COALESCE(si.refund, 0) AS total_refund FROM products p LEFT JOIN cats c ON c.id = p.cat_id LEFT JOIN suppliers s ON s.id = p.supplier_id LEFT JOIN ( SELECT SUM(total_quantity) AS total FROM stocks GROUP BY product_id ) st ON st.product_id = p.id LEFT JOIN ( SELECT SUM(quantity) AS total, SUM(CASE WHEN sale_id IN (SELECT sale_id FROM refunds) THEN quantity END) as refund FROM sale_items GROUP BY product_id ) si ON si.product_id = p.id; sale_id中是唯一的,那么您甚至可以将其加入refunds。同样:这通常没有什么区别,但是在MySQL中它仍然可以。曾经因对待sale_items子句比IN子句差得多而臭名昭著。我不知道情况可能不再如此。您可以尝试-如果FROM是唯一的)。