如何在mysql上优化select sum()?

如何在mysql上优化select sum()?,mysql,sql,select,optimization,Mysql,Sql,Select,Optimization,我有一个SQL,运行大约需要2,5秒 select SUM(valorlanca0_.VALOR_PREVISTO) as col_0_0_ from CF_VALOR_LANCADO_DETALHADO valorlanca0_ inner join CF_VALOR_LANCADO valorlanca1_ on valorlanca0_.ID_VALOR_LANCADO=valorlanca1_.ID_VALOR_LANCADO inner join CF_LANCAMENTO la

我有一个SQL,运行大约需要2,5秒

select SUM(valorlanca0_.VALOR_PREVISTO) as col_0_0_ 
from CF_VALOR_LANCADO_DETALHADO valorlanca0_ 
inner join CF_VALOR_LANCADO valorlanca1_ on valorlanca0_.ID_VALOR_LANCADO=valorlanca1_.ID_VALOR_LANCADO 
inner join CF_LANCAMENTO lancamento2_ on valorlanca1_.ID_LANCAMENTO=lancamento2_.ID_LANCAMENTO 
inner join CF_ADMINISTRACAO administra12_ on lancamento2_.ID_ADMINISTRACAO=administra12_.ID_ADMINISTRACAO 
inner join CF_EMPRESA empresa13_ on lancamento2_.ID_EMPRESA=empresa13_.ID_EMPRESA 
inner join CF_USUARIO usuario14_ on lancamento2_.ID_USUARIO_CRIOU=usuario14_.ID_USUARIO 
left outer join CF_FORMA_PAGAMENTO formapagam9_ on valorlanca1_.ID_FORMA_PAGAMENTO=formapagam9_.ID_FORMA_PAGAMENTO 
inner join CF_CONTA conta10_ on valorlanca1_.ID_CONTA=conta10_.ID_CONTA 
left outer join CF_FATURA fatura11_ on valorlanca1_.ID_FATURA=fatura11_.ID_FATURA 
left outer join CF_CATEGORIA categoria3_ on valorlanca0_.ID_CATEGORIA=categoria3_.ID_CATEGORIA 
left outer join CF_CENTRO_CUSTO centrocust4_ on valorlanca0_.ID_CENTRO_CUSTO=centrocust4_.ID_CENTRO_CUSTO 
left outer join CF_FAV_FONTE_PAGADORA favfontepa5_ on valorlanca0_.ID_FAV_FONTE_PAGADORA=favfontepa5_.ID_FAV_FONTE_PAGADORA 
left outer join CF_CONTA_CONTABIL contaconta6_ on valorlanca0_.ID_CONTA_CONTABIL=contaconta6_.ID_CONTA_CONTABIL 
left outer join CF_CONTATO contato7_ on valorlanca0_.ID_CONTATO=contato7_.ID_CONTATO 
left outer join CF_MARCA marca8_ on valorlanca0_.ID_MARCA=marca8_.ID_MARCA 
where administra12_.ID_ADMINISTRACAO=406 and lancamento2_.TIPO_CONTA=2 and (conta10_.ID_CONTA in (2060, 404, 405, 4291, 406, 410, 4292, 403, 4355, 402, 407)) and conta10_.TIPO<>9 and lancamento2_.TIPO_TRANSACAO=10 
and (valorlanca1_.SITUACAO in (1)) and ((valorlanca1_.DATA_PREVISTA<='2015-07-22' and valorlanca1_.SITUACAO=1 or valorlanca1_.DATA_BAIXA<='2015-07-22' and valorlanca1_.SITUACAO=3) 
and conta10_.TIPO<>2 or fatura11_.DATA_VENCIMENTO<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=1 or valorlanca1_.DATA_BAIXA<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=3) 
and ((empresa13_.ID_EMPRESA in (422, 3643)) and usuario14_.ID_USUARIO=574 or empresa13_.ID_EMPRESA in (422, 3643))
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
最大的表格是CF_VALOR_LANCADO(8.55亿条记录)和CF_VALOR_LANCADO_DETALHADO(8.60亿条记录)

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 

我能做些什么来优化我的选择?

那是一个地狱般的查询:)

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
您可以从删除您未使用的表开始,然后执行以下操作:

select SUM(valorlanca0_.VALOR_PREVISTO) as col_0_0_ 
from CF_VALOR_LANCADO_DETALHADO valorlanca0_ 
inner join CF_VALOR_LANCADO valorlanca1_ on valorlanca0_.ID_VALOR_LANCADO=valorlanca1_.ID_VALOR_LANCADO 
inner join CF_LANCAMENTO lancamento2_ on valorlanca1_.ID_LANCAMENTO=lancamento2_.ID_LANCAMENTO 
inner join CF_ADMINISTRACAO administra12_ on lancamento2_.ID_ADMINISTRACAO=administra12_.ID_ADMINISTRACAO 
inner join CF_EMPRESA empresa13_ on lancamento2_.ID_EMPRESA=empresa13_.ID_EMPRESA 
inner join CF_USUARIO usuario14_ on lancamento2_.ID_USUARIO_CRIOU=usuario14_.ID_USUARIO 
inner join CF_CONTA conta10_ on valorlanca1_.ID_CONTA=conta10_.ID_CONTA 
left outer join CF_FATURA fatura11_ on valorlanca1_.ID_FATURA=fatura11_.ID_FATURA 
where administra12_.ID_ADMINISTRACAO=406 
    and lancamento2_.TIPO_CONTA=2 
    and (conta10_.ID_CONTA in (2060, 404, 405, 4291, 406, 410, 4292, 403, 4355, 402, 407)) 
    and conta10_.TIPO<>9 
    and lancamento2_.TIPO_TRANSACAO=10 
    and (valorlanca1_.SITUACAO in (1)) 
    and ((valorlanca1_.DATA_PREVISTA<='2015-07-22' and valorlanca1_.SITUACAO=1 or valorlanca1_.DATA_BAIXA<='2015-07-22' and valorlanca1_.SITUACAO=3) and conta10_.TIPO<>2 
        or fatura11_.DATA_VENCIMENTO<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=1 
        or valorlanca1_.DATA_BAIXA<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=3
        )
    and ((empresa13_.ID_EMPRESA in (422, 3643)) and usuario14_.ID_USUARIO=574 or empresa13_.ID_EMPRESA in (422, 3643))
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
相当于

and empresa13_.ID_EMPRESA in (422, 3643) 
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
您也可以尝试这个更改(95%确定它们是等效的)-不会有嵌套的“或”,这在我的经验中有时有助于提高性能

and (   
(valorlanca1_.DATA_PREVISTA<='2015-07-22' and valorlanca1_.SITUACAO=1 
    or valorlanca1_.DATA_BAIXA<='2015-07-22' and valorlanca1_.SITUACAO=3
) and conta10_.TIPO<>2 
or fatura11_.DATA_VENCIMENTO<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=1 
or valorlanca1_.DATA_BAIXA<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=3
)
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
和(

(valorlanca1.DATA_PREVISTA我需要查看您在
CF_VALOR_LANCADO\u DETALHADO
CF_VALOR_LANCADO
上创建的索引,但是您是否考虑过使用
复合索引
?这允许您在多个列上创建索引

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
作为猜测,我将考虑创建一个
CF_VALOR_LANCADO.DATA_PREVISTA
CF_VALOR_LANCADO.SITUACAO

对您的查询的解释一点也不差

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
您是否可以在MySQL服务器上运行这些SQL语句,并在此处发布结果,同时发布MySQL服务器版本? 通过这些语句,我们可以看到查询是CPU限制的还是I/O限制的

SET profiling = 1;

select SUM(valorlanca0_.VALOR_PREVISTO) as col_0_0_ 
from CF_VALOR_LANCADO_DETALHADO valorlanca0_ 
inner join CF_VALOR_LANCADO valorlanca1_ on valorlanca0_.ID_VALOR_LANCADO=valorlanca1_.ID_VALOR_LANCADO 
inner join CF_LANCAMENTO lancamento2_ on valorlanca1_.ID_LANCAMENTO=lancamento2_.ID_LANCAMENTO 
inner join CF_ADMINISTRACAO administra12_ on lancamento2_.ID_ADMINISTRACAO=administra12_.ID_ADMINISTRACAO 
inner join CF_EMPRESA empresa13_ on lancamento2_.ID_EMPRESA=empresa13_.ID_EMPRESA 
inner join CF_USUARIO usuario14_ on lancamento2_.ID_USUARIO_CRIOU=usuario14_.ID_USUARIO 
left outer join CF_FORMA_PAGAMENTO formapagam9_ on valorlanca1_.ID_FORMA_PAGAMENTO=formapagam9_.ID_FORMA_PAGAMENTO 
inner join CF_CONTA conta10_ on valorlanca1_.ID_CONTA=conta10_.ID_CONTA 
left outer join CF_FATURA fatura11_ on valorlanca1_.ID_FATURA=fatura11_.ID_FATURA 
left outer join CF_CATEGORIA categoria3_ on valorlanca0_.ID_CATEGORIA=categoria3_.ID_CATEGORIA 
left outer join CF_CENTRO_CUSTO centrocust4_ on valorlanca0_.ID_CENTRO_CUSTO=centrocust4_.ID_CENTRO_CUSTO 
left outer join CF_FAV_FONTE_PAGADORA favfontepa5_ on valorlanca0_.ID_FAV_FONTE_PAGADORA=favfontepa5_.ID_FAV_FONTE_PAGADORA 
left outer join CF_CONTA_CONTABIL contaconta6_ on valorlanca0_.ID_CONTA_CONTABIL=contaconta6_.ID_CONTA_CONTABIL 
left outer join CF_CONTATO contato7_ on valorlanca0_.ID_CONTATO=contato7_.ID_CONTATO 
left outer join CF_MARCA marca8_ on valorlanca0_.ID_MARCA=marca8_.ID_MARCA 
where administra12_.ID_ADMINISTRACAO=406 and lancamento2_.TIPO_CONTA=2 and (conta10_.ID_CONTA in (2060, 404, 405, 4291, 406, 410, 4292, 403, 4355, 402, 407)) and conta10_.TIPO<>9 and lancamento2_.TIPO_TRANSACAO=10 
and (valorlanca1_.SITUACAO in (1)) and ((valorlanca1_.DATA_PREVISTA<='2015-07-22' and valorlanca1_.SITUACAO=1 or valorlanca1_.DATA_BAIXA<='2015-07-22' and valorlanca1_.SITUACAO=3) 
and conta10_.TIPO<>2 or fatura11_.DATA_VENCIMENTO<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=1 or valorlanca1_.DATA_BAIXA<='2015-07-22' and conta10_.TIPO=2 and valorlanca1_.SITUACAO=3) 
and ((empresa13_.ID_EMPRESA in (422, 3643)) and usuario14_.ID_USUARIO=574 or empresa13_.ID_EMPRESA in (422, 3643));

SHOW PROFILES; # Displays an table with records use it to find your select profile id

SHOW PROFILE ALL FOR QUERY [select profile_id]
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 

make_join_statistics()函数是一个很难运行的函数。

我的建议是拆分查询,并尝试查看什么是较慢的连接

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
也有类似的事情

and **(valorlanca1_.SITUACAO in (1))**
and ((valorlanca1_.DATA_PREVISTA<='2015-07-22' 
  and **valorlanca1_.SITUACAO=1** or valorlanca1_.DATA_BAIXA<='2015-07-22' 
  and **valorlanca1_.SITUACAO=3**) 
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
和**(第(1)段中的valorlanca1_.SITUACAO)**

和((valorlanca1_uuu.DATA_uprevista首先,我开始清理原始查询的格式,以查看表之间的相关性。然后我改为使用别名,将较长的表名简化为较短的表名,并得出了这个结论

select 
      SUM(V0.VALOR_PREVISTO) as col_0_0_ 
   from 
      CF_VALOR_LANCADO_DETALHADO V0
         inner join CF_VALOR_LANCADO V1
            on V0.ID_VALOR_LANCADO = V1.ID_VALOR_LANCADO 
            inner join CF_LANCAMENTO L2
               on V1.ID_LANCAMENTO = L2.ID_LANCAMENTO 
               inner join CF_ADMINISTRACAO A12
                  on L2.ID_ADMINISTRACAO = A12.ID_ADMINISTRACAO 
               inner join CF_EMPRESA E13
                  on L2.ID_EMPRESA = E13.ID_EMPRESA 
               inner join CF_USUARIO U14
                  on L2.ID_USUARIO_CRIOU = U14.ID_USUARIO 
            left outer join CF_FORMA_PAGAMENTO F9
               on V1.ID_FORMA_PAGAMENTO = F9.ID_FORMA_PAGAMENTO 
            inner join CF_CONTA C10
               on V1.ID_CONTA = C10.ID_CONTA 
               left outer join CF_FATURA F11
                  on V1.ID_FATURA = F11.ID_FATURA 
         left outer join CF_CATEGORIA C3
            on V0.ID_CATEGORIA = C3.ID_CATEGORIA 
         left outer join CF_CENTRO_CUSTO C4
            on V0.ID_CENTRO_CUSTO = C4.ID_CENTRO_CUSTO 
         left outer join CF_FAV_FONTE_PAGADORA F5
            on V0.ID_FAV_FONTE_PAGADORA = F5.ID_FAV_FONTE_PAGADORA 
         left outer join CF_CONTA_CONTABIL C6
            on V0.ID_CONTA_CONTABIL = C6.ID_CONTA_CONTABIL 
         left outer join CF_CONTATO C7
            on V0.ID_CONTATO = C7.ID_CONTATO 
         left outer join CF_MARCA M8
            on V0.ID_MARCA = M8.ID_MARCA 
   where 
          A12.ID_ADMINISTRACAO = 406 
      and L2.TIPO_CONTA = 2 
      and C10.ID_CONTA in (2060, 404, 405, 4291, 406, 410, 4292, 403, 4355, 402, 407)
      and C10.TIPO <> 9 
      and L2.TIPO_TRANSACAO = 10 
      and V1.SITUACAO in (1)
      and ( (    V1.DATA_PREVISTA <= '2015-07-22' 
             and V1.SITUACAO = 1 
             or  V1.DATA_BAIXA <= '2015-07-22' 
             and V1.SITUACAO=3 ) 
          and C10.TIPO <> 2 
           or F11.DATA_VENCIMENTO <= '2015-07-22' 
          and C10.TIPO = 2 
          and V1.SITUACAO = 1 
           or V1.DATA_BAIXA <= '2015-07-22' 
          and C10.TIPO = 2 
          and V1.SITUACAO = 3 ) 
      and (     E13.ID_EMPRESA in (422, 3643)
            and U14.ID_USUARIO = 574 
             or E13.ID_EMPRESA in (422, 3643) )
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
由于这里的最后一个OR条件与第一个相同,因此消除了ID_USUARIO=574的需要,并且可以简化为

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
  and E13.ID_EMPRESA in (422, 3643)
您有几个左联接(功能上与“左外联接”相同)未在任何条件中使用…请删除它们,包括

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
LEFT JOIN CF_FORMA_PAGAMENTO
left join CF_CATEGORIA 
left join CF_CENTRO_CUSTO 
left join CF_CONTA_CONTABIL 
left join CF_CONTATO 
left join CF_MARCA 

您使用的是左连接CF_FATURA fatura11_uu,并且它处于OR条件下,本质上是将其转换为内部连接,因为您不允许NULL,但是对于“DATA\u VENCIMENTO
的显式测试有时会通过转换为
UNION
进行优化:

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 
SELECT ...
    WHERE ((x=1) OR (y=2))
-->

  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 ) 

UNION DISTINCT
UNION ALL
慢,但您可能需要它进行重复数据消除。)

根据查询计划,您的选择还不错。看起来您是I/O绑定的,您的硬盘是问题所在。MySQL必须找到硬盘上的记录,这显然是机械的,而且速度很慢。您是否使用InnoDB?如果是,您的
InnoDB缓冲池大小是多少?
  and C10.TIPO <> 2 
   or (    F11.DATA_VENCIMENTO <= '2015-07-22' 
       and C10.TIPO = 2 
       and V1.SITUACAO = 1 )
   or ( V1.DATA_BAIXA <= '2015-07-22' 
        and C10.TIPO = 2 
        and V1.SITUACAO = 3 )