Оптимизация SQL-запроса - PullRequest
       1

Оптимизация SQL-запроса

0 голосов
/ 28 августа 2018

Помогите, пожалуйста, оптимизировать следующий SQL-запрос

SELECT Executor_service.*, Executor.*
FROM Executor_service
INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
LEFT JOIN Cancel ON Cancel.executor_id = Executor.user_id AND Cancel.city_id = 3538 
WHERE Cancel.order_id IS NULL
AND Executor.user_id != 236 
AND Executor_service.service_id = 511
AND Executor_service.status = 'free'
AND Executor_service.mstatus = 'work'
AND Executor_service.city_id =3538
AND Executor.balance > 0
AND Executor.status = 'free'
ORDER BY Executor.rate DESC
LIMIT 1

Этот код очень медленный (10-15 с) - >> ВЛЕВО СОЕДИНИТЬ Отмена ВКЛ. Cancel.executor_id = Executor.user_id И Cancel.city_id = 3538

ОБНОВЛЕНИЕ 1

Мне нужно взять Executor.user_id

Таблица Executor имеет следующую структуру Палач

Таблица Executor_service имеет следующую структуру Executor_service

И таблица Cancel имеет следующую структуру Отмена

Table Executor есть все исполнители в системе.

Таблица Executor_service - это существующие службы для каждого исполнителя, где Executor.user_id = Executor_service.executor_id

Таблица Отмена - это существующая информация об отмене заказа (кто отменяет и причина)

Мне нужно получить одного исполнителя с наивысшим рейтингом, а не с нулевым балансом из указанного города, который выполняет обслуживание в соответствии с указанным идентификатором из таблицы Executor_service (Executor_service.service_id). В то же время в таблице «Отмена» не должно быть записи о том, что данный исполнитель был прослушан для этой услуги

ОБНОВЛЕНИЕ 2

я меняю

SELECT Executor_service.*, Executor.*
FROM Executor_service
INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id

до

SELECT Executor.user_id
FROM Executor
INNER JOIN Executor_service ON Executor.user_id = Executor_service.executor_id

и займет 3-5 секунд, но это большое время для запроса.

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Так как вы ничего не выбираете из таблицы, которую планируете LEFT JOIN, вы можете вообще ее опустить. Использование INNER JOIN фильтрует ваши результаты, а LEFT (OUTER) JOIN - нет, поэтому в этом нет необходимости:

  SELECT Executor_service.*, Executor.*
    FROM Executor_service
         INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
   WHERE     Executor.user_id != 236
         AND Executor_service.service_id = 511
         AND Executor_service.status = 'free'
         AND Executor_service.mstatus = 'work'
         AND Executor_service.city_id = 3538
         AND Executor.balance > 0
         AND Executor.status = 'free'
ORDER BY Executor.rate DESC
    LIMIT 1

Посмотри, подходит ли тебе это. Если вы действительно планировали использовать LEFT JOIN на Cancel столе, то это путь.

Проблема с оптимизацией производительности заключается в том, что вы на самом деле вообще не используете LEFT JOIN. Если вы действительно хотите использовать LEFT JOIN, вы не можете использовать столбец этих таблиц в предложении WHERE. Это превращает это присоединение к INNER JOIN.

Еще одна небольшая вещь: глядя на ваш код, лучше связать ваши таблицы Cancel и Executor_service через общий столбец city_id, чем писать city_id =3538 дважды:

Код с этими исправлениями выглядит следующим образом (с сохранением таблицы Cancel в INNER JOIN, как это было на самом деле раньше):

  SELECT Executor_service.*, Executor.*
    FROM Executor_service
         INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
         INNER JOIN Cancel ON Cancel.executor_id = Executor.user_id AND Cancel.city_id = Executor_service.city_id
   WHERE     Cancel.order_id IS NULL
         AND Executor.user_id != 236
         AND Executor_service.service_id = 511
         AND Executor_service.status = 'free'
         AND Executor_service.mstatus = 'work'
         AND Executor_service.city_id = 3538
         AND Executor.balance > 0
         AND Executor.status = 'free'
ORDER BY Executor.rate DESC
    LIMIT 1

Кроме того, если вы хотите, чтобы пользователь LEFT JOIN отфильтровал результаты, чтобы исключить записи в таблице Cancel (как указано в комментариях), тогда вы можете использовать следующий код:

  SELECT Executor_service.*, Executor.*
    FROM Executor_service
         INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
   WHERE     Executor.user_id != 236
         AND Executor_service.service_id = 511
         AND Executor_service.status = 'free'
         AND Executor_service.mstatus = 'work'
         AND Executor_service.city_id = 3538
         AND Executor.balance > 0
         AND Executor.status = 'free'
         AND NOT EXISTS (SELECT Cancel.order_id
                           FROM Cancel
                          WHERE Cancel.executor_id = Executor.user_id AND Cancel.city_id = Executor_service.city_id)
ORDER BY Executor.rate DESC
    LIMIT 1

В качестве альтернативы вы могли бы написать свой запрос, используя LEFT JOIN (с добавлением Cancel.order_id среди выбранных значений), а затем переместить условие WHERE Cancel.order_id IS NULL во внешний запрос.

TL; DR:

Если вы действительно хотите использовать INNER JOIN для таблицы Cancel, вы можете вообще ее опустить. Если вы хотите использовать его для исключения данных, связанных с существующими данными в таблице Cancel, вам следует переписать их, как указано в последнем коде (или с помощью подзапроса). Если ничего из этого - трудно оптимизировать код, не зная данных, стоящих за ним ... В этом случае, по крайней мере, я надеюсь, что эти небольшие данные помогли вам лучше понять, что вы действительно хотите и что вы получаете.

0 голосов
/ 28 августа 2018

Немного реструктурирован запрос. Я предпочитаю видеть, где таблица «A» напрямую соединяется с «B», а «B» - с «C» и т. Д. Я также стараюсь держать слева в таблице table.column слева и справа в table.colum, чтобы право. Также критерии, специфичные для таблицы, я стараюсь ставить непосредственно при объединении. Это помогает мне визуализировать, где находятся условия и что может помочь при определении индексов для оптимизации ...

SELECT 
      ES.*, 
      E.*
   FROM 
      Executor_service ES
         INNER JOIN Executor E
            ON ES.executor_id = E.user_id
            AND ES.status = E.status
            AND E.balance > 0
         LEFT JOIN Cancel C
            ON ES.executor_id = C.executor_id
           AND ES.City_ID = C.city_id
   WHERE 
          ES.service_id = 511
      AND ES.status = 'free'
      AND ES.mstatus = 'work'
      AND ES.city_id = 3538
      AND ES.executor_id != 236
      AND C.order_id IS NULL
   ORDER BY 
      E.rate DESC
   LIMIT 1

Тем не менее, было несколько транзитивных отношений (если A = B и B = C, тогда A = C), таких как ваши критерии для userID исполнителя, который совпадает с executor_service.executor_id, поэтому я перенес это в предложение WHERE в «ES» (псевдоним executor_service). Исправьте в левом соединении ваш код отмены и явно ищите IS NULL в где.

Что касается индексов, я бы гарантировал следующее

Executor_Service table -- index on (service_id, city_id, mstatus, status )
Executor table -- index on ( user_id, status, balance ).  
Cancel table -- index on ( executor_id, city_id )

Включая баланс в индекс, он создает покрывающий индекс для запроса, который не требует перехода на страницы данных, и может получить значение непосредственно из индекса.

0 голосов
/ 28 августа 2018

Попробуйте индексировать по: executor_service(city_id, service_id, status, mstatus, user_id).

Я предполагаю, что ключи join уже имеют индексы.

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