Как повысить производительность этого запроса Sql в MySql? - PullRequest
1 голос
/ 28 января 2020

У меня есть запрос в 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;

Result explain

Как улучшить SQL, чтобы получить более быстрый результат?

Ответы [ 2 ]

1 голос
/ 29 января 2020

Коррелированные подзапросы, такие как ваш подзапрос 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.

1 голос
/ 28 января 2020
  • 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 для необязательных / неизвестных / et c, значений.

  • 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.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...