繁体   English   中英

如何提高MySql中这个查询Sql的性能?

[英]How to improve the performance of this query Sql in MySql?

我在 SQL 中有一个查询需要大约 15 秒才能返回结果,我需要提高性能,我已经工作了几个小时,但无法得到令人满意的结果。 MySql 5.6 表 lote:304053 行表 Prod_lista:41525 行时间结果:15 秒

SET @CODIGO_EMPRESA = 1;
SET @CODIGO_FILIAL = 1;
SET @PESQUISA = '%';


SELECT 
    B.CODIGO_PRODUTO,
    C.DESCRICAO,
    B.SALDO_DISPONIVEL,
    B.SALDO_RESERVADO,
    B.SALDO_INDISPONIVEL,
    B.SALDO_TERCEIROS,
    B.SALDO_ENTREGUE,
    C.SITUACAO
FROM
    (SELECT 
        A.CODIGO_EMPRESA,
            A.CODIGO_FILIAL,
            A.CODIGO_PRODUTO,
            SUM(A.SALDO_DISPONIVEL) AS SALDO_DISPONIVEL,
            SUM(A.SALDO_RESERVADO) AS SALDO_RESERVADO,
            SUM(A.SALDO_INDISPONIVEL) AS SALDO_INDISPONIVEL,
            SUM(A.SALDO_TERCEIROS) AS SALDO_TERCEIROS,
            SUM(A.SALDO_ENTREGUE) AS SALDO_ENTREGUE
    FROM
        LOTE A
    WHERE
        A.CODIGO_EMPRESA = @CODIGO_EMPRESA
            AND A.CODIGO_FILIAL = @CODIGO_FILIAL
            AND IFNULL(A.ENCERRADO, 0) = 0
            AND A.TIPO NOT IN (4 , 5)
    GROUP BY A.CODIGO_PRODUTO
    ORDER BY NULL) B
        INNER JOIN
    PROD_LISTA C ON B.CODIGO_EMPRESA = C.CODIGO_EMPRESA
        AND B.CODIGO_FILIAL = C.CODIGO_FILIAL
        AND B.CODIGO_PRODUTO = C.CODIGO
        AND IFNULL(C.SITUACAO, 1) = 1
WHERE
    (B.CODIGO_PRODUTO LIKE @PESQUISA
        OR C.CODIGOFABRICA LIKE @PESQUISA
        OR C.CODIGOBARRA_COMPLETO LIKE @PESQUISA
        OR C.DESCRICAO LIKE @PESQUISA
        OR EXISTS( SELECT 
            D.CODIGOPRODUTO
        FROM
            PROD_CODIGO_BARRA D
        WHERE
            D.CODIGO_EMPRESA = @CODIGO_EMPRESA
                AND D.CODIGO_FILIAL = @CODIGO_FILIAL
                AND B.CODIGO_PRODUTO = D.CODIGOPRODUTO
                AND D.CODIGOBARRA_COMPLETO LIKE @PESQUISA))
LIMIT 0 , 100

批处理表的结构

CREATE TABLE `lote` (
  `CODIGO_EMPRESA` int(3) unsigned NOT NULL,
  `CODIGO_FILIAL` int(4) unsigned NOT NULL,
  `CODIGO_LOTE` bigint(20) NOT NULL,
  `CODIGO_LOTE_PAI` bigint(20) DEFAULT NULL,
  `NUMERO_LOTE` varchar(15) DEFAULT NULL,
  `TIPO` int(2) DEFAULT NULL ,
  `DATAHORA` datetime DEFAULT NULL,
  `CODIGO_DOCUMENTO` bigint(20) DEFAULT NULL,
  `CODIGO_ITEM` int(5) DEFAULT NULL,
  `CUSTO` decimal(21,10) DEFAULT '0.0000000000',
  `CODIGO_PRODUTO` varchar(25) DEFAULT NULL,
  `DESTINO_INICIAL` int(1) DEFAULT NULL,
  `SALDO_INICIAL` decimal(15,4) DEFAULT '0.0000',
  `SALDO_DISPONIVEL` decimal(15,4) DEFAULT '0.0000',
  `SALDO_INDISPONIVEL` decimal(15,4) DEFAULT '0.0000',
  `SALDO_TERCEIROS` decimal(15,4) DEFAULT '0.0000',
  `SALDO_RESERVADO` decimal(15,4) DEFAULT '0.0000',
  `SALDO_ENTREGUE` decimal(15,4) DEFAULT '0.0000',
  `VENCIMENTO` date DEFAULT NULL,
  `ENCERRADO` int(1) DEFAULT '0',
  `CODIGO_CONTAGEM` bigint(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_LOTE`),
  KEY `IDX_CODIGO_LOTE` (`CODIGO_LOTE`),
  KEY `IDX_CODIGO_PRODUTO` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_PRODUTO`),
  KEY `IDX_CONSULTA_PDV` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_PRODUTO`,`ENCERRADO`,`TIPO`),
  KEY `IDX_CONTAGEM_ESTOQUE` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_PRODUTO`,`TIPO`,`ENCERRADO`),
  KEY `IDX_CUSTO` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CUSTO`),
  KEY `IDX_DOCUMENTO_ITEM` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_DOCUMENTO`,`CODIGO_ITEM`),
  KEY `IDX_EMPRESA_FILIAL` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`),
  KEY `IDX_ESTOQUE` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`TIPO`,`ENCERRADO`),
  KEY `IDX_FILTRO` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_LOTE`,`DATAHORA`),
  KEY `IDX_LOTE_COMP` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`CODIGO_PRODUTO`,`DATAHORA`),
  KEY `IDX_TIPO` (`TIPO`),
  KEY `IDX_TIPO_CODIGO` (`CODIGO_EMPRESA`,`CODIGO_FILIAL`,`TIPO`,`CODIGO_PRODUTO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

结果说明

如何改进 SQL 以获得更快的结果?

  • OR是性能杀手; 尽量避免它。

  • B.CODIGO_PRODUTO LIKE "%"等价于TRUE ,但优化器没有看到。 动态构造查询而不是使用@variables 并选择消除子句的值会好得多。

  • 同样重要的是,当没有前导通配符时,优化器可能能够使用索引:

     x LIKE 'A%' -- possibly uses index x LIKE '%' -- cannot use index x LIKE '%B' -- cannot use index x LIKE '@foo' -- cannot use index
  • int(4) - (4)无关紧要。 所有INTs都是相同的 4 字节数据类型。 对于较小的数据类型,请参阅SMALLINT和类似的。

  • 不要盲目使用NULL (您的大多数列都是NULLable ); 为可选/未知/等值保留NULL

  • IFNULL(A.ENCERRADO, 0) = 0 -- 如果可以将其安排为具有 2 个值(0 和 1),则可以简化此表达式并避免使用函数。 函数使表达式不是 'sargable'。 完成后,下面建议的索引可能会有用。

  • 当您有INDEX(a,b,c) ,就不需要也有INDEX(a,b) 例如: IDX_EMPRESA_FILIAL可以删除。

  • 这些索引可能有帮助:

     D: (CODIGOPRODUTO, CODIGO_FILIAL, CODIGO_EMPRESA, CODIGOBARRA_COMPLETO) A: (ENCERRADO, CODIGO_FILIAL, TIPO, CODIGO_EMPRESA)
  • 可能还有更多建议。 应用以上大部分内容,然后回来获取更多建议。 (并提供其他SHOW CREATE TABLEs 。)

相关子查询,如您的 EXISTS 查询,如果它们与来自外部查询的大量结果相关联,则可能代价高昂。 我建议将您的 EXISTS 条件转换为这样的:

OR B.CODIGO_PRODUTO IN (
      SELECT D.CODIGOPRODUTO
      FROM PROD_CODIGO_BARRA AS D
      WHERE D.CODIGO_EMPRESA = @CODIGO_EMPRESA
         AND D.CODIGO_FILIAL = @CODIGO_FILIAL
         AND D.CODIGOBARRA_COMPLETO LIKE @PESQUISA
   )

对于您的相关版本,子查询最终会针对来自主 FROM 的每一行进行单独评估。 但是对于这个不相关的版本,子查询只被评估一次,它的结果集用于检查来自主 FROM 的行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM