如何加快这个(相对简单的)查询速度

时间:2016-09-26 11:12:27

标签: mysql

我有一个相对简单的查询,它简单地总结了我的网站已接受的所有paypal付款,使用每天更新的货币转换表将它们全部转换为GBP。

SELECT SUM(PPI.mc_gross * IF(PPI.mc_currency='GBP', 1, CC.fConv))

FROM paypal_payment_info PPI

JOIN currency_conversions CC ON CC.sFrom = PPI.mc_currency AND CC.tConv = DATE(PPI.tIPN)

在PPI中有30,000行,在CC中有5,000行,查询需要大约9秒,在一个功能强大的盒子上。

EXPLAIN显示了这个:

id  select_type table   type    possible_keys   key key_len ref rows Extra
1   SIMPLE  PPI ALL                 31271   
1   SIMPLE  CC  ref sFrom   sFrom   9   db.PPI.mc_currency  1167    Using where

我在tIPN & mc_currency上尝试了一个索引,在mc_currency & tIPN上尝试了一个索引。没有帮助。我想也许DATE()函数是问题,所以创建了一个列tIPNdate并修改了相关的索引来代替使用它,它没有任何区别。

我觉得我错过了一些明显的东西,有人可以帮忙吗?

由于

3 个答案:

答案 0 :(得分:1)

对于这么多数据而言,9秒并不是极端性能问题。

话虽如此,将其分解为两个不同的SUM()操作可能是有意义的。

首先,查询GBP交易。

SELECT SUM(PPI.mc_gross)
  FROM paypal_payment_info PPI
 WHERE PPI.mc_currency = 'GBP'

(mc_currency, mc_gross)上的复合索引将使查询尽可能快。这可能会使您获得大部分交易而无需任何连接。希望更快。

然后,处理非英镑交易。

SELECT SUM(PPI.mc_gross * CC.fConv)
  FROM paypal_payment_info PPI
  JOIN currency_conversions CC ON CC.sFrom = PPI.mc_currency 
                             AND CC.tConv = DATE(PPI.tIPN)
 WHERE PPI.mc_currency <> 'GBP'

要清除JOIN的不可分割的DATE()部分,请执行以下操作:

SELECT SUM(PPI.mc_gross * CC.fConv)
  FROM paypal_payment_info PPI
  JOIN currency_conversions CC ON PPI.mc_currency = CC.sFrom
                              AND PPI.tPN >= CC.tConv
                              AND PPI.tPN <  CC.tConv + INTERVAL 1 DAY
 WHERE PPI.mc_currency <> 'GBP'

不确定,我认为paypal_payment_info( tPN, mc_currency, mc_gross)上的复合索引将有助于此查询。我认为currency_conversions (sFrom, tConv, fConv)上的索引也会有所帮助。

然后,您可以使用UNION ALL和另一个SUM添加两个查询的结果。

SELECT SUM(sums) sums
  FROM (
              SELECT SUM(PPI.mc_gross) sums
                FROM paypal_payment_info PPI
               WHERE PPI.mc_currency = 'GBP'
      UNION ALL
              SELECT SUM(PPI.mc_gross * CC.fConv) sums
                FROM paypal_payment_info PPI
                JOIN currency_conversions CC ON PPI.mc_currency = CC.sFrom
                                            AND PPI.tPN >= CC.tConv
                                            AND PPI.tPN <  CC.tConv + INTERVAL 1 DAY
               WHERE PPI.mc_currency <> 'GBP'
       ) s

答案 1 :(得分:1)

首先,我会通过在您的货币转换表中添加一行来select转换为自己1.00的费率来简化GBP表达式。

接下来,恢复您的tIPNdate列(值为DATE(PPI.tIPN)) - 这肯定有帮助。

您需要currency_conversions上的索引:

create index index_cc_001 on currency_conversions(tconv, sFrom, fConv);

会对您的查询产生最大影响。此外,索引中列的顺序很重要 - 始终首先放置最多变且完全匹配的列。

paypal_payment_info上创建索引:

create index index_ppi_001 on paypal_payment_info(tIPNdate, mc_currency, mc_gross);

这两个索引都是覆盖索引,这意味着可以在索引中找到查询所需的所有数据,从而避免了访问表的需要。

查询应如下(删除冗余部分后):

SELECT SUM(mc_gross * fConv)
FROM paypal_payment_info
JOIN currency_conversions ON sFrom = mc_currency
    AND tConv = tIPNdate

最后,您可能会发现扭转表的顺序效果更好:

SELECT SUM(mc_gross * fConv)
FROM currency_conversions
JOIN paypal_payment_info ON sFrom = mc_currency
    AND tConv = tIPNdate

答案 2 :(得分:0)

SELECT SUM(PPI.mc_gross * IF(PPI.mc_currency='GBP', 1, CC.fConv))

FROM paypal_payment_info PPI

JOIN currency_conversions CC ON CC.sFrom = PPI.mc_currency AND CC.tConv = DATE(PPI.tIPN) FORCE INDEX (tIPN,mc_currency)