Мой SQL - попытка оптимизации Query возвращает больше строк - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть один запрос, который я унаследовал от моего предыдущего коллеги, но мне нужно его оптимизировать.

Этот запрос возвращает 72 строки.

SELECT  id, contract_no, customer, address, cm_mac, aps
    FROM  
    (
        SELECT  *
            from  new_installed_devices
            where  insert4date >='2018-10-28'
              AND  insert4date <='2018-10-28'
              AND  install_mark<2
    ) as d1
    left join  
    (
        SELECT  *
            from  
            (
                SELECT  contract_no AS c_no, cm_mac AS c_mc, MIN(tstamp) as time2,
                        sum(1) as aps
                    from  devices_change
                    where  contract_no in (
                        SELECT  distinct(contract_no)
                            from  devices_change
                            where  tstamp >= '2018-10-28 06:59:59'
                              AND  tstamp <= '2018-10-29 07:00:00'
                          )
                    group by  contract_no, cm_mac 
            ) as mtmbl
            where  mtmbl.time2 >= '2018-10-28 06:59:59'
              and  mtmbl.time2 <= '2018-10-29 07:00:00' 
    ) as tmp  ON d1.contract_no=tmp.c_no
    where  aps>0
    group by  contract_no, customer, address, cm_mac;

Требуется 20 секунд для выполнения. Я переписываю его, пытаясь оптимизировать, но в этом случае у меня есть 75 строк (возвращаются 3 дополнительные строки), но результат представляется через 2 секунды.

Я сделал так (разница только в одном подзапросе):

SELECT  id, contract_no, customer, address, cm_mac, aps
    FROM  
    (
        SELECT  *
            from  new_installed_devices
            where  insert4date >='2018-10-28'
              AND  insert4date <='2018-10-28'
              AND  install_mark<2
    ) as d1
    left join  
    (
        SELECT  *
            from  
            (
                SELECT distinct
                        (contract_no) AS c_no,
                        cm_mac AS c_mc, MIN(tstamp) as time2,
                        sum(1) as aps
                    from  devices_change
                    where  tstamp >= '2018-10-28 06:59:59'
                      AND  tstamp <= '2018-10-29 07:00:00'
                    group by  contract_no, cm_mac 
            ) as mtmbl
            where  mtmbl.time2 >= '2018-10-28 06:59:59'
              and  mtmbl.time2 <= '2018-10-29 07:00:00' 
    ) as tmp  ON d1.contract_no=tmp.c_no
    where  aps>0
    group by  contract_no, customer, address, cm_mac;

Как видите, я не сильно изменился в моем случае, но все же получаю больше строк, чем должно быть в результате. Может кто-нибудь, пожалуйста, сообщите мне причину, почему мой второй запрос не возвращает полностью правильный результат. Я перепробовал много вещей для оптимизации, но безуспешно. Большое спасибо !!!

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018
  • Первый подзапрос в предложении FROM можно заменить прямой ссылкой на таблицу new_installed_devices с некоторыми условиями в предложении WHERE. В более старых версиях MySQL не очень хорошо обрабатывает подзапросы, поэтому старайтесь избегать их в предложении FROM (особенно если у вас их больше 1 или 2).
  • Условия диапазона для mtmbl.time2 можно сложить в условие HAVING подзапроса, чтобы обеспечить фильтрацию этих данных как можно быстрее, без создания большой временной таблицы с этим подзапросом.
  • Можете ли вы предоставить SHOW CREATE TABLE для этих таблиц и EXPLAIN для запроса? Это может быть полезно.

При угадывании порядка, который здесь выберет MySQL, вы можете попытаться добавить эти индексы и выполнить следующий запрос, чтобы проверить, работает ли он лучше. Я применил приведенные выше рекомендации к запросу ниже (надеюсь, мои предположения о происхождении столбцов были правильными, в противном случае, пожалуйста, исправьте все соответствующим образом):

ALTER TABLE `devices_change` ADD INDEX `devices_change_idx_no_mac_tstamp` (`contract_no`,`cm_mac`,`tstamp`);
ALTER TABLE `devices_change` ADD INDEX `devices_change_idx_tstamp_no` (`tstamp`,`contract_no`);
ALTER TABLE `new_installed_devices` ADD INDEX `new_installed_device_idx_no_insert4date` (`contract_no`,`insert4date`);

Запрос:

SELECT
        new_installed_devices.id,
        new_installed_devices.contract_no,
        new_installed_devices.customer,
        new_installed_devices.address,
        new_installed_devices.cm_mac,
        new_installed_devices.aps 
    FROM
        new_installed_devices AS d1 
    LEFT JOIN
        (
            SELECT
                * 
            FROM
                (SELECT
                    devices_change.contract_no AS c_no,
                    devices_change.cm_mac AS c_mc,
                    MIN(devices_change.tstamp) AS time2,
                    sum(1) AS aps 
                FROM
                    devices_change 
                WHERE
                    devices_change.contract_no IN (
                        SELECT
                            DISTINCT (devices_change.contract_no) 
                        FROM
                            devices_change 
                        WHERE
                            devices_change.tstamp >= '2018-10-28 06:59:59' 
                            AND devices_change.tstamp <= '2018-10-29 07:00:00'
                    ) 
                GROUP BY
                    devices_change.contract_no,
                    devices_change.cm_mac 
                HAVING
                    devices_change.time2 >= '2018-10-28 06:59:59' 
                    AND devices_change.time2 <= '2018-10-29 07:00:00' 
                ORDER BY
                    NULL) AS mtmbl) AS tmp 
                    ON d1.contract_no = tmp.c_no 
            WHERE
                aps > 0 
                AND d1.insert4date >= '2018-10-28' 
                AND d1.insert4date <= '2018-10-28' 
                AND d1.install_mark < 2 
            GROUP BY
                new_installed_devices.contract_no,
                new_installed_devices.customer,
                new_installed_devices.address,
                new_installed_devices.cm_mac 
            ORDER BY
                NULL
0 голосов
/ 02 ноября 2018
  • Не используйте SELECT *, когда вам не нужны все столбцы. Похоже, contract_no - единственный столбец, необходимый из dl, следовательно, из new_installed_devices.
  • Есть ли какая-то причина для проверки insert4date на равенство таким странным образом?
  • Рекомендовать INDEX(insert4date, install_mark, dl) (в таком порядке)
  • Старайтесь избегать конструкции IN ( SELECT ... ). Обычно лучше использовать EXISTS или LEFT JOIN.
  • Не говорите DISTINCT(contract_no), ... - DISTINCT - это , а не функция; его эффект распространяется на весь набор выражений. Избавьтесь от DISTINCT, так как GROUP BY имеет такой эффект.
  • Рекомендовать INDEX(contract_no, cm_max, tstamp) (в таком порядке)
  • Проверка на mtmbl.time2 является избыточной, поскольку `MIN (tstamp) уже ограничен этим временным диапазоном (1 день + 2 секунды).
  • Пожалуйста, предоставьте SHOW CREATE TABLE.
...