【问题标题】:Mysql query taking 20 minutes only for 11000 records. how to optimize below mysql query select query in where clause with not exists仅 11000 条记录的 Mysql 查询需要 20 分钟。如何在不存在的where子句中优化mysql查询选择查询
【发布时间】:2025-11-27 10:30:01
【问题描述】:
SELECT DISTINCT ACA.Application_No, AC.FirstName,AC.Id,AC.LastName,AC.MobileNo,CL.leadId FROM ABSLI_PAYMENT_TRANSACTION APT 内部加入 ABSLI_CUSTOMER_APPLICATION ACA ON ACA.Policy_No=APT.policyId AC.Id=ACA.CustomerId 内部加入 ABSLI_CUSTOMER AC 左加入 ABSLI_CUSTOMER_LEAD CL ON CL.policyId = ACA.Policy_No 内部加入 ABSLI_Policy_Status_Tracking pst ON pst.policyId = APT.policyId WHERE APT.paymentStatus='Y' 并且不存在(从 ABSLI_SERVICE_STATUS 中选择 1,其中 PolicyNo=APT.policyId AND NAME = 'APEX_Validate') AND ACA.Application_No NOT IN (SELECT RT.ApplicationNumber FROM ABSLI_REFUND_TRANSACTION RT WHERE RT.Status != 'Retain') 按 pst.updatedDate DESC 排序;

【问题讨论】:

  • “为什么我的查询很慢”问题应该包括查询计划,如果我们要提供任何有意义的建议
  • ABSLI_REFUND_TRANSACTION.ApplicationNumber 列上有索引吗?
  • @BarbarosÖzhan 是的。
  • ..此外,DISTINCT 跨越多个表,这些表永远无法优化,因为它总是需要一个临时表来处理和可能的排序以获得正确的结果,你可以优化的最好的是 ON , WHERE 和 ORDER BY 子句..
  • 请提供SHOW CREATE TABLE

标签: mysql sql query-performance


【解决方案1】:

考虑到您在ABSLI_REFUND_TRANSACTION.ApplicationNumber 列上有一个索引,您可以尝试将NOT EXISTS 也用于第二个子查询,而不是NOT IN。这样,您可以为该子查询使用此索引,并且通常NOT IN 对于大量数据可能会出现问题。

SELECT DISTINCT ACA.Application_No, AC.FirstName, AC.Id, AC.LastName, AC.MobileNo, CL.leadId
  FROM ABSLI_PAYMENT_TRANSACTION APT
 INNER JOIN ABSLI_CUSTOMER_APPLICATION ACA
    ON ACA.Policy_No = APT.policyId
 INNER JOIN ABSLI_CUSTOMER AC
    ON AC.Id = ACA.CustomerId
  LEFT JOIN ABSLI_CUSTOMER_LEAD CL
    ON CL.policyId = ACA.Policy_No
 INNER JOIN ABSLI_Policy_Status_Tracking pst
    ON pst.policyId = APT.policyId
 WHERE APT.paymentStatus = 'Y'
   AND NOT EXISTS (SELECT 1
                     FROM ABSLI_SERVICE_STATUS
                    WHERE PolicyNo = APT.policyId
                      AND NAME = 'APEX_Validate')
   AND NOT EXISTS (SELECT 1
                     FROM ABSLI_REFUND_TRANSACTION RT
                    WHERE RT.Status != 'Retain'
                      AND RT.ApplicationNumber = ACA.Application_No)
 ORDER BY pst.updatedDate DESC;

但在不知道执行计划的情况下,很难说出更多关于性能的信息。

【讨论】:

  • @RaymondNijland 是的,我带他们去GROUP BY 列表
  • 别介意我的评论我现在喝啤酒,我没有注意到.. 但是 GROUP BY 同样重要,因为 GROUP BY 跨越多个表,它总是需要一个临时表来处理并且可能排序以获得正确的结果
  • @RaymondNijland 我不确定,你也许是对的。在 Oracle 中,我目睹了 GROUP BY 的表现比 DISTINCT 高,也许 OP 可能会经历 :) 在不知道解释计划的情况下... bla bla bla ....
  • “在 Oracle 中,我见证了 GROUP BY 的性能优于 DISTINCT,” 在 MySQL 中 DISTINCT 和 GROUP BY 或多或少相同,请参阅手册 DISTINCT Optimization
  • 很好@RaymondNijland,那么我们不需要考虑这种情况,谢谢。
【解决方案2】:

在较大的数据集上,相关子查询的成本可能很高,您可能想尝试转换

...
AND NOT EXISTS (SELECT 1 FROM ABSLI_SERVICE_STATUS WHERE PolicyNo=APT.policyId AND NAME = 'APEX_Validate')
...

类似

...
LEFT JOIN ABSLI_SERVICE_STATUS AS ss ON APT.policyId = ss.PolicyNo AND ss.NAME = 'APEX_Validate'
...
AND ss.NAME IS NULL

通常,我建议使用ss 中的“id”字段进行 IS NULL 检查,但 NAME 显然存在(根据您的查询)并且不能同时为 'APEX_Validate' 和 NULL。此外,如果PolicyNo, Name 上存在复合索引,则可能无需访问表本身就可以使用该索引。)

【讨论】:

  • NOT IN 同上。