子查询减慢更新速度

时间:2018-12-13 10:39:05

标签: mysql indexing mysql-dependent-subquery

此查询引用了约3M条记录,现在需要绝对时间才能运行。 数据取自Excel电子表格,其中“客户/发票”向下行,而月度值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';

EXPLAIN产生:

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

从视觉上看,它表示这是一次全索引扫描,我认为红色背景表明这很不好。

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

3 个答案:

答案 0 :(得分:0)

尝试将子查询的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';

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

答案 1 :(得分:0)

您可以尝试以下方法吗? 使用CTE或临时表并使用case和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
)

答案 2 :(得分:0)

您有此综合索引吗?还是从这两列开始以更宽的索引开始

INDEX(CustomerRef, TransMonth)   -- in either order

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

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