【问题标题】:Why is this JOIN inefficent?为什么这个 JOIN 效率低下?
【发布时间】:2012-03-17 02:55:49
【问题描述】:

我正在尝试一次从多个表中获取数据。在我的旧系统中,我会进行查询,获取所需的数据,然后将其用于下一个查询。我想避免进行多个查询,所以我将它们与连接结合起来:

 SELECT `to`.`id` , `to`.`group`, `to`.`email`, `pTo`.`min_offer`, COUNT(`offerRes`.`id`), `settingsRes`.`email`
                                FROM `members` AS `to`

                                LEFT JOIN (
                                  SELECT `id` , `min_offer`
                                  FROM `profile`
                                ) pTo ON pTo.id = to.id

                                LEFT JOIN (
                                  SELECT `id`, `from`,`to`
                                  FROM `offers`
                                ) offerRes ON offerRes.from = ? && offerRes.to = to.id

                                LEFT JOIN (
                                  SELECT `id`, `email`
                                  FROM `user_settings`
                                )  settingsRes ON settingsRes.id = to.id

                                WHERE to.id = ? LIMIT 1

我使用传入的 ID (to.id) 从配置文件表、报价表和用户设置表中获取结果。这些表都有一个PRIMARY id,所有内容都映射到该ID。我的 PHP 用户准备了语句,但是我替换了 ?在 PHPMyAdmin 中具有硬值。我的解释如下:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY         to     const    PRIMARY       PRIMARY   4         const     1   
1   PRIMARY     <derived2>  ALL     NULL            NULL    NULL    NULL        45  
1   PRIMARY     <derived3>  ALL     NULL            NULL    NULL    NULL        1   
1   PRIMARY     <derived4>  ALL     NULL            NULL    NULL    NULL        15  
4   DERIVED     user_settingsALL    NULL            NULL    NULL    NULL        15  
3   DERIVED     offers      system  NULL            NULL    NULL    NULL        1   
2   DERIVED     profile     ALL     NULL            NULL    NULL    NULL        45  

由于我的查询基于主键 id,为什么 MySQL 可以执行简单的查找,但它会遍历配置文件字段中的所有 45 行?有什么办法让这个更有效吗?我想要的只是以下表格:

id (from members) - this is optional as this is what is obtained from the user and passed in to SELECT against
group (from members) - corresponding to the id that is passed in
email (from members) - corresponding to the id that is passed in
min_offer (from profile) - also corresponding to the id that is passed in
NUM_ROWS(from offers) - number of rows corresponding to the id taht is passed in and another number that is also passed in.  The offers table has a PRIMARY id but it is a different ID than what is passed in.
email (from user_settings) - corresponding to the id that is passed in

【问题讨论】:

  • 非INNER连接越多,查询效率越低。
  • 据我所知,如果这些子查询中的任何一个不返回任何行,那么整个事情都会返回 NULL。这是不可取的。我仍然不明白为什么它在 idCol = someOtherIdCol 上执行 LEFT JOIN 时必须搜索所有 45 条记录。 ID 是主键。除非我读错了 EXPLAIN,否则它不应该搜索 45 行?
  • 左连接应该检查 45 行。表成员的所有行都会在结果中,你找不到另一个表对应的行会有空字段
  • @Ignacio - 这种情况下的效率与连接的类型无关,它是使用派生表,可以直接访问表!
  • 所以你告诉我执行多个查询会更快? SE:ECT f1,f2 FROM memnbers WHERE id = ?限制 1 ... 选择 f1 FROM profile WHERE id = ?限制 1. 至少这将利用主键并进行快速查找。任何将它们结合起来以提高代码美观和效率的方法(更少的 mysql 调用 = 我认为更好)

标签: php mysql optimization join


【解决方案1】:

由于我的查询是基于 PRIMARY 键 id 的,为什么 MySQL 可以进行简单的查找时会遍历配置文件字段中的所有 45 行?

因为您要加入子查询的结果,这将包括表中的每条记录:

LEFT JOIN (
  SELECT `id` , `min_offer`
  FROM `profile`
) pTo ON pTo.id = to.id

-- etc.

改为直接加入表:

LEFT JOIN `profile` AS pTo ON pTo.id = to.id

LEFT JOIN `offers` AS offerRes ON offerRes.from = ? && offerRes.to = to.id

LEFT JOIN `user_settings` AS settingsRes ON settingsRes.id = to.id

【讨论】:

  • 完美。非常高效并且运行正常。我不知道如何使用 JOIN。我假设您需要在 JOINed 查询中指定您想要的字段,以便您以后可以“访问”这些变量(即现在我可以在另一个查询中使用 pTo.min_offer)。现在我看到你可以这样做,为什么世界上会有人像我以前那样使用子查询?这将大量查询合并为 1 并且仍然保持效率。
猜你喜欢
  • 1970-01-01
  • 2012-06-18
  • 2013-12-18
  • 1970-01-01
  • 2019-06-21
  • 2021-04-09
  • 2023-04-04
  • 2021-11-29
  • 1970-01-01
相关资源
最近更新 更多