我在以下查询中遇到了性能问题:
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 :(得分: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'
您是否看到我们如何将相关摘要子查询更改为自己的虚拟表?这使得查询计划程序只运行一次该查询,而不是主查询中的每一行运行一次。这有很大帮助。
对于缺少任何相应IFNULL()
行的transaction
行,customer_payment_options
会为您获取cpo_payment_amount的数值,而不是NULL。
transaction
(local_branch_id, source_country_id, date_added)
表上的复合索引将有助于此查询;查询引擎可以随机访问local_branch_id
和source_country_id
值,然后范围扫描date_added
值。
你是如何自学的? http://use-the-index-luke.com/是一个好的开始。
答案 1 :(得分: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
。