2016-04-06 144 views
1

我在下面的查询性能问题:MySQL查询性能提高

SELECT t.local_branch_revenue, t.total_payment, 

    (SELECT SUM(IF(cpo.real_account_type = 'HQ', 0, cpo.payment_amount)) AS cpo_payment_amount 
     FROM customer_payment_options cpo 
     WHERE tran_id=t.id 
     AND cpo.payment_type != 'WALLET' AND cpo.payment_type != 'REWARD_CREDIT' 
     GROUP BY cpo.tran_id) 
    as cpo_payment_amount, 

    b.ben_firstname, b.ben_lastname 
    FROM transaction t 
    LEFT JOIN beneficiary b 
    ON b.id=t.ben_id 
    WHERE t.local_branch_id='31' 
    AND DATE(t.date_added) < '2016-04-07' 
    AND source_country_id='40' 
    AND t.transaction_status != 'CANCELLED' 

EXPLAIN

+----+--------------------+-------+--------+----------------------------------------+----------------------------------------+---------+-----------------+------+-------------+ 
| id | select_type  | table | type | possible_keys       | key         | key_len | ref    | rows | Extra  | 
+----+--------------------+-------+--------+----------------------------------------+----------------------------------------+---------+-----------------+------+-------------+ 
| 1 | PRIMARY   | t  | ref | local_branch_id,source_country_id  | local_branch_id      | 5  | const   | 2 | Using where | 
+----+--------------------+-------+--------+----------------------------------------+----------------------------------------+---------+-----------------+------+-------------+ 
| 1 | PRIMARY   | b  | eq_ref | PRIMARY        | PRIMARY        | 8  | mtesdb.t.ben_id | 1 |    | 
+----+--------------------+-------+--------+----------------------------------------+----------------------------------------+---------+-----------------+------+-------------+ 
| 2 | DEPENDENT SUBQUERY | cpo | ref | tran_id_payment_type_real_account_type | tran_id_payment_type_real_account_type | 9  | mtesdb.t.id  | 1 | Using where | 
+----+--------------------+-------+--------+----------------------------------------+----------------------------------------+---------+-----------------+------+-------------+ 

正如你所看到的,它使用从可能的关键指标。但仍然需要大约13秒的查询。

我也有索引transaction表:(ben_id, company_id, source_country_id, date_added, tran_owner)。但是,它甚至没有进入可能的密钥部分。

让我知道你是否需要table模式。

我在这里错过了什么?

+0

行数不准确。因为它在开发服务器上。查询来自prod服务器。 –

+0

“customer_payment_options”,“transaction”和“beneficiary”的索引定义是什么?每个表中有多少行,在13秒查询后返回多少行? –

+0

你能解释一下真正的行数吗?只用t表尝试查询。日期(...)可能是个问题 – quazardous

回答

2

从属子查询在MySQL中表现不佳...查询规划器不会将它们转换为有效的JOINed子查询。 (在Oracle和SQL Server中它们都可以,但谁拥有这些资金?)所以,最好的选择是重构查询以消除依赖子查询。

这里是你的子查询。让我们把它作为一个独立的子查询来重构。我们将摆脱WHERE tran_id=t.id并稍后将其移至ON条款。

   SELECT tran_id, 
        SUM(IF(real_account_type = 'HQ', 
          0, 
          payment_amount)) AS cpo_payment_amount 
       FROM customer_payment_options 
       WHERE payment_type != 'WALLET' 
       AND payment_type != 'REWARD_CREDIT' 
      GROUP BY tran_id 

通知可以简化这个如下 - 你IF()子句排除与real_account_type = 'HQ'行。相反,您可以在WHERE子句中执行此操作。

   SELECT tran_id, 
        SUM(payment_amount) AS cpo_payment_amount 
       FROM customer_payment_options 
       WHERE payment_type != 'WALLET' 
       AND payment_type != 'REWARD_CREDIT' 
       AND real_account_type != 'HQ' 
      GROUP BY tran_id 

(tran_id, payment_type, real_account_type, payment_amount)一个复合索引可以帮助这个子查询的运行速度。但是这三个!=子句的存在保证了完整的索引扫描;没有办法随机访问这些索引。

这会生成一个虚拟表,其中包含一行,每个tran_id包含您需要的总和。

接下来我们需要将它加入到主查询中。

SELECT t.local_branch_revenue, 
     t.total_payment, 
     IFNULL(cposum.cpo_payment_amount,0) cpo_payment_amount, 
     b.ben_firstname, b.ben_lastname 
    FROM transaction t 
    LEFT JOIN beneficiary b ON b.id=t.ben_id 
    LEFT JOIN (
      SELECT tran_id, 
        SUM(payment_amount) AS cpo_payment_amount 
       FROM customer_payment_options 
       WHERE payment_type != 'WALLET' 
       AND payment_type != 'REWARD_CREDIT' 
       AND real_account_type != 'HQ' 
      GROUP BY tran_id 
     ) cposum ON t.id = cposum.tran_id 
WHERE t.local_branch_id='31' 
    AND DATE(t.date_added) < '2016-04-07' 
    AND source_country_id='40' 
    AND t.transaction_status != 'CANCELLED' 

您是否看到我们如何将依赖性摘要子查询更改为其自己的虚拟表?这让查询计划程序只运行一次该查询,而不是主查询中的每行一次。这有很大帮助。

对于transaction行,缺少任何相应的customer_payment_options行,IFNULL()会为您提供cpo_payment_amount的数值,而不是NULL。

(local_branch_id, source_country_id, date_added)上的transaction表上的复合索引将有助于此查询;查询引擎可以随机访问local_branch_idsource_country_id值,然后范围扫描date_added值。

你如何学会自己做这件事? http://use-the-index-luke.com/是一个好的开始。

0
WHERE t.local_branch_id='31' 
AND DATE(t.date_added) < '2016-04-07' 
AND source_country_id='40' 

将日期测试更改为简单t.date_added < '2016-04-07'!否则,以下索引建议将不起作用。

什么表是source_country_id在?如果它在t,那么你需要INDEX(local_branch_id, source_country_id, date_added)。如果它不在t,那么INDEX(local_branch_id, date_added)

如果您需要进一步讨论,请提供SHOW CREATE TABLE