【问题标题】:Slow MySQL query, EXPLAIN shows Using temporary; Using filesortMySQL查询慢,EXPLAIN显示Using temporary;使用文件排序
【发布时间】:2023-04-09 02:33:01
【问题描述】:

这个查询:

EXPLAIN SELECT ppi_loan.customerID,
               loan_number,
               CONCAT(forename, ' ', surname) AS agent,
               name,
               broker,
              (SELECT timestamp
               FROM ppi_sar_status
               WHERE history = 0
                   AND (status = 10 || status = 13)
                   AND ppi_sar_status.loanID = ppi_loan.loanID) AS ppi_unsure_date,
              fosSent,
              letterSent,
              (SELECT timestamp
               FROM ppi_ques_status
               WHERE status = 1 
                   AND ppi_ques_status.loanID = ppi_loan.loanID
               ORDER BY timestamp DESC LIMIT 1) AS sent_date,
               ppi_ques_status.timestamp
FROM ppi_loan
LEFT JOIN ppi_assignments ON ppi_assignments.customerID = ppi_loan.customerID
LEFT JOIN italk.users ON italk.users.id = agentID
LEFT JOIN ppi_ques_status ON ppi_ques_status.loanID = ppi_loan.loanID
JOIN ppi_lenders ON ppi_lenders.id = ppi_loan.lender
JOIN ppi_status ON ppi_status.customerID = ppi_loan.customerID
JOIN ppi_statuses ON ppi_statuses.status = ppi_status.status
   AND ppi_ques_status.status = 1
   AND ppi_ques_status.history = 0
   AND (cc_type = '' || (cc_type != '' AND cc_accepted = 'no'))
   AND ppi_loan.deleted = 'no'
   AND ppi_loan.customerID != 10
GROUP BY ppi_loan.customerID, loan_number

非常慢,这里是 EXPLAIN 查询的所有结果

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   PRIMARY ppi_ques_status ref loanID,status,history   status  3   const   91086   Using where; Using temporary; Using filesort
1   PRIMARY ppi_loan    eq_ref  PRIMARY,customerID  PRIMARY 8   ppimm.ppi_ques_status.loanID    1   Using where
1   PRIMARY ppi_lenders eq_ref  PRIMARY PRIMARY 4   ppimm.ppi_loan.lender   1   Using where
1   PRIMARY ppi_assignments eq_ref  customerID  customerID  8   ppimm.ppi_loan.customerID   1   
1   PRIMARY users   eq_ref  PRIMARY PRIMARY 8   ppimm.ppi_assignments.agentID   1   
1   PRIMARY ppi_status  ref status,customerID   customerID  8   ppimm.ppi_loan.customerID   6   
1   PRIMARY ppi_statuses    eq_ref  PRIMARY PRIMARY 4   ppimm.ppi_status.status 1   Using where; Using index
3   DEPENDENT SUBQUERY  ppi_ques_status ref loanID,status   loanID  8   func    1   Using where; Using filesort
2   DEPENDENT SUBQUERY  ppi_sar_status  ref loanID,status,history   loanID  8   func    2   Using where

为什么要扫描这么多行以及为什么“使用临时;使用文件排序”? 我无法删除任何子查询,因为我需要它们产生的所有结果

【问题讨论】:

  • 很明显,您只有在 ppi_ques_status 表中具有单字段索引,而您需要一个多字段索引才能真正帮助您的查询。
  • 它会扫描那么多行,因为那么多行满足您为数据库提供的条件。 using where 表示它正在应用一个索引来减少它必须查找的行数,using filesort 在那里,因为它正在逐位排序记录以按照您指定的顺序返回它们(filesort 只是命名错误)。它很慢,因为它从磁盘(慢)而不是内存(快)读取。
  • @mjh 您对说明的解释与实际含义相去甚远。
  • 能否请您添加from 子句中您未指明表名的字段来自哪个表,例如loan_number
  • @mjh 请参阅 MySQL 手册以了解解释结果:dev.mysql.com/doc/refman/5.7/en/explain-output.html。比如你写了It scans that many rows because that many rows satisfy the condition you gave your database.实际上是这样的:The rows column indicates the number of rows MySQL believes it must examine to execute the query. For InnoDB tables, this number is an estimate, and may not always be exact.你可以检查所有其他的,你都错了。

标签: mysql join explain


【解决方案1】:

正如评论中已经提到的,查询缓慢的主要原因是您似乎只有单列索引,而您需要多列索引来覆盖连接、过滤器和分组依据。

此外,您的查询还有 2 个其他问题:

    1234563 MySQL 确实允许在某些 sql 模式设置下运行此类查询,但它们仍然违反 sql 标准并且可能会产生意想不到的副作用,除非您真的知道自己在做什么。
  1. 您在连接条件中的 ppi_loan 表上具有过滤器,该表是 left join 中的左表。由于左连接的性质,这些记录不会从结果集中删除,但 MySQL 不会连接它们上的任何值。这些条件应移至where 子句。

我要创建的索引:

  • ppi_sar_status:loanID、状态、历史字段的多列索引 - 我会考虑将其移至连接部分,因为该表不存在

  • ppi_ques_status:loanID、状态、时间戳字段的多列索引 - 这将支持子查询和连接。请记住,子查询在说明中也有文件排序。

  • ppi_loan:至少在 customerID、loan_number 字段上的多列索引以支持 group by 子句,因此至少避免文件排序。您可以考虑根据对这个索引的选择性在连接条件中添加其他字段。

我也不确定为什么您在联接中有最后 2 个状态表,因为您没有从它们中检索任何值。如果您要使用这些表来消除某些记录,请考虑使用exists() 子查询而不是连接。在连接中,MySQL 需要从所有连接的表中获取数据,而在exists() 子查询中,它只会检查结果集中是否存在至少 1 条记录,而不会从基础表中检索任何实际数据。

【讨论】:

  • 查询很慢,因为它们受 I/O / CPU 限制,或者当有大量数据要返回时,必须缓冲然后通过网络发送。您的答案仅涉及索引。这对于要发送的纯粹 数量 数据没有任何作用,只有更有效的方法可以减少浪费多少 I/O 来查找记录。 90k 行甚至 1m 行对于今天的 CPU 来说都不算什么,这清楚地表明使用了默认的 innodb 配置。尽管索引可以变得更有效,但您只关注问题的一部分。此外,子查询 === JOIN。我不会对此投票。
  • 谢谢Shadow,这很有帮助,我明天再看看
  • @mjh 如 OP 所示,查询产生 755 条记录,因此通过网络缓冲和结束不是问题。子查询与联接不同。
  • 子查询 is 与 join 完全相同。它将被解析和执行,就像你要编写一个连接一样。在这种情况下,这无关紧要,因为提供的答案似乎已经足够了,剩下的就是测量 e-peen。在我看来,如果答案中包含额外内容(配置 MySQL),那么这个答案将是完整的。
  • @N.B.我很抱歉,但仅仅因为你在这个论点中是少数派并不能使你自动正确。关系代数是一回事,实际执行计划是另一回事。更不用说这样一个事实,有些子查询不能在没有子查询的情况下被重写为连接。
猜你喜欢
  • 2014-09-22
  • 2014-01-04
  • 1970-01-01
  • 2013-09-15
  • 1970-01-01
  • 2011-02-15
  • 2015-09-14
  • 1970-01-01
  • 2011-06-14
相关资源
最近更新 更多