簡體   English   中英

mysql查詢性能提高

[英]mysql query performance improve

我在以下查詢中有性能問題:

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'

說明

 +----+--------------------+-------+--------+----------------------------------------+----------------------------------------+---------+-----------------+------+-------------+
| 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架構。

我在這里想念什么?

依賴的子查詢在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包含一行, 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'

您是否看到我們如何將依賴的摘要子查詢更改為其自己的虛擬表? 這樣,查詢計划者就可以只運行一次該查詢,而不是對主查詢中的每一行運行一次。 這很有幫助。

對於缺少任何對應的customer_payment_options行的transaction行, IFNULL()您提供cpo_payment_amount的數字值,而不是NULL。

transaction表上的復合索引(local_branch_id, source_country_id, date_added)將有助於此查詢; 查詢引擎可以隨機訪問local_branch_idsource_country_id值,然后對date_added值進行范圍掃描。

您如何學會自己做到這一點? http://use-the-index-luke.com/是一個好的開始。

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) 如果不在tINDEX(local_branch_id, date_added)

如果您需要進一步討論,請提供SHOW CREATE TABLE

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM