为什么此MYSQL查询中的子查询比此子查询慢得多?

时间:2019-01-16 12:01:57

标签: mysql performance subquery left-join

我有一个脚本,用于从客户那里获取与发票有关的数据。每个发票和订单都有一个将其与特定用户相关联的卡代码。有时,用户拥有多个与同一卡号相关联的用户帐户;在这种情况下,我将不得不使用最近使用过的用户帐户中的用户数据,这将通过子查询获得。

SELECT invoices.*,card.*,client.* 
FROM `orders` 
LEFT JOIN `invoices` ON `invoices`.`invoice_key` =`orders`.`invoice_key` 
LEFT JOIN `cards` `card` ON `card`.`card_code` = `orders`.`card_code` 
LEFT JOIN `users` `client` ON `client`.`id` = (
     SELECT MIN(c.id) c_id FROM `users` `c` 
     WHERE (`card`.`card_code` COLLATE utf8_unicode_ci) = `c`.`card_code`
     AND  `c`.`active` = 1 ORDER BY `c`.`last_login` DESC LIMIT 1
)
WHERE `orders`.`date` BETWEEN %start_date% AND %end_date%

这很好,但是后来我实现了一个系统,在该系统中,用户可以使用多个卡代码;为了解决这个问题,我创建了另一个将用户链接到卡的表(用户卡)。并非所有用户都在此处输入信息。大多数只需要使用其默认卡代码。

SELECT invoices.*,card.*,client.* 
FROM `orders` 
LEFT JOIN `invoices` ON `invoices`.`invoice_key` =`orders`.`invoice_key` 
LEFT JOIN `cards` `card` ON `card`.`card_code` = `orders`.`card_code` 
LEFT JOIN `usercards` ON `usercards`.`card_code` = `card`.`card_code` 
LEFT JOIN `users` `client` ON (`usercards`.`user_id` IS NOT NULL 
   AND `client`.`id` = `usercards`.`user_id`) 
OR (`usercards`.`user_id` IS NULL AND `client`.`id` = (
     SELECT MIN(c.id) c_id FROM `users` `c` 
     WHERE (`card`.`card_code` COLLATE utf8_unicode_ci) = `c`.`card_code`  
       AND  `c`.`active` = 1 ORDER BY `c`.`last_login` DESC LIMIT 1)
)
WHERE `orders`.`date` BETWEEN %start_date% AND %end_date%

但是,这导致查询运行很多较慢-几分钟之前不到一秒钟。我知道它与子查询有关(如果我忽略了那一部分,只是简单地连接到该client.card_code = card.card_code的所有地方,它就可以快速运行,但是当然数据是错误的),但是我无法确定为何我看不到有任何迹象表明需要处理的数据呈指数增长。

有很多订单和发票,但是用户卡中只有很少的条目。另外,usercards.card_code是唯一索引。

1 个答案:

答案 0 :(得分:1)

我有太多问题无法直接回答。

  • OR是性能杀手。该 可以很好地转换为UNION。如果是这样,IS [not] NULL子句也许可以同时简化。
  • MINLIMIT 1相互斗争。我怀疑您应该摆脱MIN
  • 除非计划在“正确的”表中缺少行,否则不要使用LEFT
  • 请参阅hereusercards索引提示。
  • usercards.user_id IS NULL似乎是一个新条件;不会改变结果吗?
  • 修复card_code列之一的排序规则以匹配另一列。您所拥有的要么打败了索引的使用,要么放慢了速度。
  • 考虑将查询翻转。也就是说,通过找到其他表的最新ID then JOIN(假设不需要LEFT)来开始
  • INDEX(card_code, active, last_login, id)-按此顺序。
  • (可能还有更多;请修复其中一些;然后让我们进一步讨论。)
  • (使用较短的别名-避免混乱。)

有关更多讨论,请提供

SHOW CREATE TABLE -- for each table
EXPLAIN SELECT -- for each query being discussed