Php 提高mysql查询性能

Php 提高mysql查询性能,php,mysql,performance,aggregate-functions,query-performance,Php,Mysql,Performance,Aggregate Functions,Query Performance,我在以下查询中遇到性能问题: 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.paymen

我在以下查询中遇到性能问题:

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'
正如您所看到的,它使用了来自可能键的索引。但查询仍然需要大约13秒

我还对事务表进行了索引:ben_id、company_id、source_country_id、date_added、tran_owner。但是,它甚至没有出现在可能的关键部分

如果您需要表模式,请告诉我


我在这里遗漏了什么?

依赖子查询在MySQL中执行得不太好。。。查询计划器不能有效地将它们转换为连接的子查询。在Oracle和SQL Server中,它们还可以,但是谁有足够的钱来做这些呢?因此,一个很好的选择是重构查询以消除依赖子查询

这是您的子查询。让我们将其重构为一个独立的子查询。我们将去掉WHERE tran_id=t.id,稍后将其移动到ON子句

请注意,您可以将其简化如下-您的IF子句排除real\u account\u 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
交易id、付款类型、真实账户类型、付款金额上的复合索引可能有助于此子查询更快地运行。但那三个人的存在!=子句保证了完整的索引扫描;没有办法随机访问这些数据的任何索引

这将生成一个虚拟表,其中每个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'
您看到我们如何将依赖的摘要子查询更改为它自己的虚拟表了吗?这样,查询计划器只需运行该查询一次,而不是对主查询中的每一行运行一次。这很有帮助

对于缺少任何相应客户付款选项行的交易行,IFNULL为您获取cpo付款金额的数值,而不是NULL

在事务表上添加一个关于本地分支机构id、来源国id、日期id的复合索引将有助于此查询;查询引擎可以随机访问本地分支机构id和来源国家id值,然后对日期添加值进行范围扫描

你是如何学会自己做这件事的?这是一个好的开始

WHERE t.local_branch_id='31'
AND DATE(t.date_added) < '2016-04-07'
AND source_country_id='40'
将该日期测试更改为简单的t。添加日期<'2016-04-07'!否则,以下索引建议将不起作用

来源国id在哪个表格中??如果在t中,则需要索引本地分支机构id、来源国家id、添加日期。如果不在t中,则添加索引本地分支id、日期


如果需要进一步讨论,请提供“显示创建表”。

行不准确。因为它在dev服务器上。查询来自prod server。客户付款选项、交易和受益人的索引定义是什么?每个表中有多少行,在13秒的查询后返回多少行?您能用实际行数进行解释吗?尝试仅使用t表进行查询。日期。。。可能是一个问题,并把表结构和索引
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'
WHERE t.local_branch_id='31'
AND DATE(t.date_added) < '2016-04-07'
AND source_country_id='40'