【问题标题】:MySQL in clause slow with 10 or more itemsMySQL in 子句慢,有 10 个或更多项目
【发布时间】:2018-10-05 15:47:49
【问题描述】:

这个查询需要 18 秒

SELECT `wd`.`week` AS `start_week`, `wd`.`hold_code`, COUNT(wd.hold_code) AS hold_code_count
FROM `weekly_data` AS `wd`
JOIN aol_reporting_hold_codes hc ON hc.hold_code = wd.hold_code AND chart = 'GR'
WHERE `wd`.`days` <= 6 
AND `wd`.`hold_code` IS NOT NULL 
AND NOT `wd`.`hold_code` = '' 
AND `wd`.`week` >= '201717' 
AND `wd`.`itemgroup` IN ('BOTDTO', 'BOTDWG', 'C&FORG', 'C&FOTO', 'MF-SUB', 'MI-SUB', 'PROPRI', 'PROPTO', 'STRSTO', 'STRSUB') 
AND `production_type` = 2
AND `contract` = "1234"
AND `project` = 8
GROUP BY `start_week`, `wd`.`hold_code`

此查询需要 4 秒

SELECT `wd`.`week` AS `start_week`, `wd`.`hold_code`, COUNT(wd.hold_code) AS hold_code_count
FROM `weekly_data` AS `wd`
JOIN aol_reporting_hold_codes hc ON hc.hold_code = wd.hold_code AND chart = 'GR'
WHERE `wd`.`days` <= 6 
AND `wd`.`hold_code` IS NOT NULL 
AND NOT `wd`.`hold_code` = '' 
AND `wd`.`week` >= '201717' 
AND `wd`.`itemgroup` IN ('BOTDWG', 'C&FORG', 'C&FOTO', 'MF-SUB', 'MI-SUB', 'PROPRI', 'PROPTO', 'STRSTO', 'STRSUB') 
AND `production_type` = 2
AND `contract` = "1234"
AND `project` = 8
GROUP BY `start_week`, `wd`.`hold_code`

我所做的只是从 IN 子句中删除了一项。我可以删除任何一项。只要有 9 个或更少的项目,它就会在 4 秒内运行。我增加到 10 个项目需要 18 秒才能运行。

我认为 MySQL 按大小限制命令长度,即 1MB

【问题讨论】:

  • 你检查执行计划了吗?
  • 我们需要知道执行计划(知道索引也会有所帮助)。稍微改变查询很可能会导致不同的执行计划。不过,我的猜测是您在 weekly_data.itemgroup 上缺少索引,但为什么需要这么长时间取决于用于搜索条件的其他列上的数据和索引。
  • @MattGibson 谢谢,我刚刚运行解释,发现在我的 IN 子句中使用 9 选项时,使用的键是“contract,project__idx,production_type” 当我添加第十个键时,键就是 itemgroup。不确定解决方案是什么,我确实已经在 itemgroup 上有了一个密钥。除非有其他更好的建议,否则我可能会删除并重新添加它。
  • 我不会删除并重新添加索引。你需要弄清楚为什么优化器猜错了;可能是统计数据需要刷新,或者您最好添加另一个更合适的索引,但该索引是什么取决于您的数据。
  • (在此阶段要考虑的一件事:WHERE 子句的哪些单独部分会最大程度地缩小结果?即,您的哪个比较从潜在的完整中过滤掉了最多的行数结果集?你有合适的索引吗?例如,如果你在week 上没有索引,但你的表中有十年的数据,那么你可能关注的是错误的问题:D )

标签: mysql sql performance in-clause


【解决方案1】:

不仅仅是EXPLAIN,还可以使用EXPLAIN FORMAT=JSON 并获取查询的“优化器跟踪”。我怀疑IN 的长度会导致选择不同的查询计划。

IN 中的项目数量几乎没有限制。我见过多达 70K。

除此之外,您甚至可以加快 4 秒版本...

我建议有这个索引。 Grrr ...我无法分辨哪些列在哪些表中。所以,如果这些都在一个表中,那么做这样一个索引:

INDEX(production_type, contract, project) -- in any order

如果这些都在 wd 中,则在第 4 列添加 - weekitemgroupdays 中的任何一个。

小心COUNT(wd.hold_code)

  • COUNT(x) 检查x 是否非NULL;那是你要的吗?如果没有,那就直接说COUNT(*)
  • JOINing,然后GROUP BY,你会得到一个“explode-implode”。中间行数大;那就是执行COUNT 的时候。
  • COUNT(hold_code)GROUP BY hold_code 似乎都错了。你想做什么?

如需进一步讨论,请提供SHOW CREATE TABLEEXPLAIN

【讨论】:

    【解决方案2】:

    请注意 MySql IN 子句限制是用 max_allowed_pa​​cket 值建立的。如果结果更快,您可以检查 NOT IN。此外,我建议将要使用 IN 子句检查的值放在缓冲区字符串而不是逗号分隔值下,然后试一试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-01
      • 2014-12-27
      • 1970-01-01
      • 2013-01-26
      • 2020-10-22
      • 2013-05-20
      • 1970-01-01
      • 2012-01-17
      相关资源
      最近更新 更多