简体   繁体   English

MySQL是否优化相关表上的选定聚合以避免N + 1?

[英]Does MySQL optimize selected aggregations on related tables to avoid N+1?

Is this query optimal in MySQL? 这个查询在MySQL中最优吗? I mean: Is there a constant amount of queries being executed? 我的意思是:是否有恒定数量的查询正在执行?

OR does it fall in the N+1 problem? 还是它属于N + 1问题? Found nothing too detailed in the official MySQL docs regarding optimization. 在官方MySQL文档中没有发现关于优化的任何细节。

SELECT t.*, (SELECT COUNT(1) from related_table rt where rt.t_id = t.id)
FROM table t

In a naive sight, there's a query and N queries, so it would fall in the N+1 problem. 天真地看,有一个查询和N个查询,因此它属于N + 1问题。

Does MySQL 5.5+ automagically+internally improve this query to make a constant number of queries? MySQL 5.5+是否会在内部自动地+改进此查询以进行恒定数量的查询? perhaps transforming it internally to something like: 也许将其内部转换为:

SELECT t.*, COUNT(rt.id)
FROM table t LEFT OUTER JOIN related_table rt
GROUP BY t.id

I mean: I know how to improve it by hand, but I'm asking this because: 我的意思是:我知道如何手动进行改进,但是我之所以这样问是因为:

  1. Perhaps making an apportation to a framework with an (somehow incomplete IMHO) ORM via a library. 也许通过一个库对一个(有些不完整的恕我直言)ORM的框架进行了分配。
  2. Curiosity. 好奇心。 Found not so much documentation in the official MySQL docs. 在官方的MySQL文档中找不到太多的文档。

No, a correlated subquery in the select-list is not optimized out by MySQL. 不,MySQL没有优化选择列表中的相关子查询。

You can confirm this by using EXPLAIN to get a report of the optimization plan. 您可以使用EXPLAIN来获取优化计划的报告来确认这一点。 Here's a similar query using a test database: 这是一个使用测试数据库的类似查询:

mysql> explain select *, (SELECT COUNT(*) FROM cast_info where cast_info.role_id = role_type.id) AS c 
    from role_type\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: role_type
   partitions: NULL
         type: index
possible_keys: NULL
          key: role
      key_len: 98
          ref: NULL
         rows: 12
     filtered: 100.00
        Extra: Using index
*************************** 2. row ***************************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: cast_info
   partitions: NULL
         type: ref
possible_keys: cr
          key: cr
      key_len: 4
          ref: imdb.role_type.id
         rows: 2534411
     filtered: 100.00
        Extra: Using index

The select type of DEPENDENT SUBQUERY means that the subquery will be executed many times, probably once for each row of the outer query. DEPENDENT SUBQUERY的选择类型意味着该子查询将执行多次,可能对外部查询的每一行执行一次。

Compare with the EXPLAIN for the manually optimized query: 与EXPLAIN进行比较,以进行手动优化的查询:

mysql> explain select r.*, COUNT(c.id) AS c from role_type AS r  left outer join cast_info as c on r.id = c.role_id group by r.id\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: r
   partitions: NULL
         type: index
possible_keys: PRIMARY,role
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 12
     filtered: 100.00
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: c
   partitions: NULL
         type: ref
possible_keys: cr
          key: cr
      key_len: 4
          ref: imdb.r.id
         rows: 2534411
     filtered: 100.00
        Extra: Using index

This shows the access to the second table is just a simple join by reference. 这表明对第二张表的访问只是一个简单的引用联接。

You can also test with the MySQL query profiler . 您也可以使用MySQL查询分析器进行测试。 Here's the second query, that uses join: 这是第二个查询,它使用join:

+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000167 |
| checking permissions | 0.000015 |
| checking permissions | 0.000016 |
| Opening tables       | 0.000050 |
| init                 | 0.000059 |
| System lock          | 0.000044 |
| optimizing           | 0.000011 |
| statistics           | 0.000151 |
| preparing            | 0.000099 |
| Sorting result       | 0.000019 |
| executing            | 0.000010 |
| Sending data         | 9.700879 |
| end                  | 0.000024 |
| query end            | 0.000022 |
| closing tables       | 0.000017 |
| freeing items        | 0.000243 |
| cleaning up          | 0.000056 |
+----------------------+----------+

And here's the one with the dependent subquery: 这是带有依赖子查询的子查询:

+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000152 |
| checking permissions | 0.000014 |
| checking permissions | 0.000013 |
| Opening tables       | 0.000050 |
| init                 | 0.000067 |
| System lock          | 0.000042 |
| optimizing           | 0.000010 |
| statistics           | 0.000367 |
| preparing            | 0.000033 |
| optimizing           | 0.000015 |
| statistics           | 0.000032 |
| preparing            | 0.000020 |
| executing            | 0.000010 |
| Sending data         | 0.000191 |
| executing            | 0.000010 |
| Sending data         | 4.103899 |
| executing            | 0.000018 |
| Sending data         | 2.413570 |
| executing            | 0.000018 |
| Sending data         | 0.043924 |
| executing            | 0.000022 |
| Sending data         | 0.037834 |
| executing            | 0.000020 |
| Sending data         | 0.014127 |
| executing            | 0.000021 |
| Sending data         | 0.089977 |
| executing            | 0.000023 |
| Sending data         | 0.045968 |
| executing            | 0.000024 |
| Sending data         | 0.000044 |
| executing            | 0.000005 |
| Sending data         | 0.190935 |
| executing            | 0.000034 |
| Sending data         | 1.046394 |
| executing            | 0.000018 |
| Sending data         | 0.017567 |
| executing            | 0.000021 |
| Sending data         | 0.882959 |
| end                  | 0.000046 |
| query end            | 0.000023 |
| closing tables       | 0.000018 |
| freeing items        | 0.000248 |
| cleaning up          | 0.000025 |
+----------------------+----------+

You can see that the subquery causes multiple executions. 您可以看到子查询导致多次执行。 In my case, I had just a few rows in the role_type table, but if you have hundreds or thousands, the number of subquery executions can get so long that the profiler truncates that report. 以我为例,role_type表中只有几行,但是如果您有成百上千的子查询执行次数,则可能会很长,以至于探查器会截断该报告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM