【问题标题】:Subquery slowing down update子查询减慢更新
【发布时间】:2019-05-14 13:23:03
【问题描述】:

此查询引用了约 3M 条记录,现在需要一个绝对时间才能运行。 数据取自 Excel 电子表格,其中 Cust/Invoices 向下行,每月值 2016 到当前列。

此查询检查相同/不同产品在同一月份是否有值,如果可以忽略则输出 1,如果后续查询应考虑输出 0。

我已经为产品设置了一个索引,它负责初始条件,但它是绝对杀死这个的子查询:

    UPDATE tbl_transactions a
    SET ProdInCust_Mnth_Same_SameProd_LowerVal =
        CASE WHEN
                (
                    SELECT COUNT(TransactionID) 
                    FROM tbl_transactions_tmp b 
                    WHERE 
                        b.TransactionID<>a.TransactionID AND
                        b.CustomerRef=a.CustomerRef AND
                        b.TransMonth=a.TransMonth AND
                        (
                            (
                                (b.Product='PLATINUM') AND
                                b.TransValue<0
                            )
                            OR                                  
                            (
                                a.TransValue=0 AND
                                (b.Product='PLATINUM' OR b.Product='GOLD' OR b.Product='SILVER') AND
                                b.TransValue<0
                            )
                            OR
                            (
                                a.TransValue<0 AND
                                (b.Product='PLATINUM' OR b.Product='GOLD') AND
                                ((b.TransValue=a.TransValue AND b.RowReference>a.RowReference) OR
                                    b.TransValue<a.TransValue
                                )
                            )
                        )
                )>0 THEN 1 ELSE 0 END   
    WHERE Product='GOLD';

解释产生:

id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra
1   UPDATE  a   \N  index   IDX_tbl_transactions_Product    PRIMARY 8   \N  2828152 100 Using where
2   DEPENDENT SUBQUERY  b   \N  ref IX_Transactions_SP_ProcessTransAA   IX_Transactions_SP_ProcessTransAA   45  finance.a.CustomerRef,finance.a.TransMonth  1   20.7    Using where; Using index

从视觉上看,它说这是一个完整的索引扫描,我假设红色背景表明这很糟糕。

关于如何进一步优化的任何想法。

【问题讨论】:

  • 您可能想在这里发布一个更简单的问题。
  • 这是最小版本。它最初是一个大脚本,涵盖了其他三种产品,但我将它分解成更简单的块。我将把它(这部分)进一步分解为三个单独的语句,以拼命尝试让它发挥作用。
  • 尝试为TransValue添加索引并使用EXISTS而不是使用COUNT计数。
  • 请提供SHOW CREATE TABLE

标签: mysql indexing mysql-dependent-subquery


【解决方案1】:

你有这个复合索引吗?还是以这两列开始更宽的索引?

INDEX(CustomerRef, TransMonth)   -- in either order

是否可以每月计算一次上个月的信息或部分信息?将其存储在汇总表中,然后查询该表可能是一种更快的方法。

您是否碰巧知道您是否受 I/O 限制?如果您是 I/O-bound,innodb_buffer_pool_size 的值是多少,您有多少 RAM,表有多大 (GB)?

【讨论】:

    【解决方案2】:

    你能试试下面这样的东西吗? 使用 CTE 或临时表并使用 case 和 where 评估预期结果。在更新 put 适当的 where 类时使用此表值。 希望这可以帮助您创建查询。查询可能不会给出准确的结果,但可以帮助您创建查询。

    UPDATE  a
        SET ProdInCust_Mnth_Same_SameProd_LowerVal = c.val
        tbl_transactions a
        JOIN cte c on a.TransactionID = c.TransactionID        
        --WHERE Product='GOLD';
    
    WITH cte AS
    (
    SELECT b.TransactionID,  b.CustomerRef,b.TransMonth,b.TransValue, COUNT(TransactionID) ,
        case when  COUNT(TransactionID) > 0 then 1 else 0 END as val
                        FROM tbl_transactions_tmp b 
                        WHERE 
                           -- b.TransactionID<>a.TransactionID AND
                           -- b.CustomerRef=a.CustomerRef AND
                            b.TransMonth=a.TransMonth AND
                            (
                                (
                                    (b.Product='PLATINUM') AND  b.TransValue<0
                                )
                                OR                                  
                                (
                                    b.TransValue=0 AND
                                    (b.Product='PLATINUM' OR b.Product='GOLD' OR b.Product='SILVER') AND
                                    b.TransValue<0
                                )
                                OR
                                (
                                    b.TransValue<0 AND
                                    (b.Product='PLATINUM' OR b.Product='GOLD') AND
                                    ((b.TransValue=a.TransValue AND b.RowReference>a.RowReference) OR
                                        b.TransValue<a.TransValue
                                    )
                                )
                            )
                    )
                    group by b.CustomerRef
    )
    

    【讨论】:

      【解决方案3】:

      尝试将子查询的WHERE子句中使用的所有字段的索引添加到两个表中,并使用EXISTS而不是COUNT

      UPDATE tbl_transactions a
      SET ProdInCust_Mnth_Same_SameProd_LowerVal =
          CASE WHEN EXISTS
                  (
                      SELECT TransactionID
                      FROM tbl_transactions_tmp b 
                      WHERE 
                          b.TransactionID<>a.TransactionID AND
                          b.CustomerRef=a.CustomerRef AND
                          b.TransMonth=a.TransMonth AND
                          (
                              (
                                  (b.Product='PLATINUM') AND
                                  b.TransValue<0
                              )
                              OR                                  
                              (
                                  a.TransValue=0 AND
                                  (b.Product='PLATINUM' OR b.Product='GOLD' OR b.Product='SILVER') AND
                                  b.TransValue<0
                              )
                              OR
                              (
                                  a.TransValue<0 AND
                                  (b.Product='PLATINUM' OR b.Product='GOLD') AND
                                  ((b.TransValue=a.TransValue AND b.RowReference>a.RowReference) OR
                                      b.TransValue<a.TransValue
                                  )
                              )
                          )
                  ) THEN 1 ELSE 0 END   
      WHERE Product='GOLD';
      

      参考:Is EXISTS more efficient than COUNT(*)>0?

      【讨论】:

        猜你喜欢
        • 2017-12-24
        • 2021-10-30
        • 2021-01-30
        • 1970-01-01
        • 2017-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多