Проблемы с производительностью по SQL-запросу с подобными вложенными элементами - PullRequest
0 голосов
/ 02 июля 2018

У меня сложный SQL-запрос, который работает, но его производительность ниже идеальной (выполнение занимает почти 3 секунды). Думаю, я оптимизировал все, что мог придумать, но, может быть, есть кое-что еще, что я до сих пор не могу поймать.

Вот оно:

SELECT DISTINCT doctors.doc_id, doctors.doc_title, doctors.doc_last, 
    doctors.doc_first, doctors.doc_email, doctors.doc_birthday,
    doctors.doc_mobile, doctors.doc_occasional, doctors.doc_fiscal_code, 
    doctors.doc_register_number, doctors.doc_register_province, 
    doctors.doc_agreement, doctors.doc_notes, doctors.doc_timestamp,
    doctors.doc_deleted, specializations.spe_id, specializations.spe_name,
    activities.act_id, activities.act_name,
    users.use_id, users.use_last, users.use_first, users.use_active,
    (SELECT COUNT(*)
    FROM congress
    INNER JOIN participants ON participants.par_congress = congress.cng_id
    WHERE par_doctor = doc_id
    AND congress.cng_from >= '2018-01-01'
    AND congress.cng_from <= '2018-07-02')
    AS cng_count,
    (SELECT COUNT(*)
    FROM visits
    INNER JOIN reports ON reports.rep_id = visits.vis_report
    INNER JOIN locations ON locations.loc_id = visits.vis_location
    WHERE visits.vis_doctor = doctors.doc_id 
    AND locations.loc_structure LIKE '%' 
    AND reports.rep_dated >= '2018-01-01' 
    AND reports.rep_dated <= '2018-07-02')
    AS vis_count_all,
    (SELECT COUNT(*)
    FROM visits
    INNER JOIN reports ON reports.rep_id = visits.vis_report
    INNER JOIN locations ON locations.loc_id = visits.vis_location 
    WHERE visits.vis_doctor = doctors.doc_id 
    AND reports.rep_user = users.use_id 
    AND locations.loc_structure LIKE '%' 
    AND reports.rep_dated >= '2018-01-01' 
    AND reports.rep_dated <= '2018-07-02')
    AS vis_count_user,
    (SELECT COUNT(*)
    FROM locations
    WHERE locations.loc_doctor = doctors.doc_id )
    AS loc_count
    FROM doctors
    LEFT JOIN locations ON locations.loc_doctor = doctors.doc_id
    INNER JOIN specializations ON specializations.spe_id = doctors.doc_specialization
    INNER JOIN activities ON activities.act_id = doctors.doc_activity
    INNER JOIN users ON users.use_id = doctors.doc_user 
    WHERE doctors.doc_last IS NOT NULL 
    AND doctors.doc_id LIKE '%' 
    AND (locations.loc_province IS NULL OR locations.loc_province LIKE '%') 
    AND (locations.loc_structure IS NULL OR locations.loc_structure LIKE '%') 
    AND DATE(doctors.doc_timestamp) <= '2018-07-02' 
    AND doctors.doc_occasional LIKE '%' 
    AND doctors.doc_deleted LIKE '0' 
    AND doctors.doc_agreement LIKE '%' 
    AND doctors.doc_active 
    AND users.use_id LIKE '%' 
    GROUP BY doctors.doc_id 
    HAVING vis_count_user <> - 1 
    ORDER BY doctors.doc_last, doctors.doc_first, doctors.doc_id

Реальное узкое место находится на подвыборах vis_count_all и vis_count_user (они отличаются только для дополнительного оператора AND reports.rep_user = users.use_id): удаление их ускоряет запрос

Поскольку они отличаются только утверждением, я не знаю, смогу ли я использовать один из них, чтобы получить другое значение более простым способом

Во всяком случае, я создал все ключи, которые мог придумать, вот результат EXPLAIN

enter image description here

Пожалуйста, какие-нибудь советы по улучшению? Спасибо

1 Ответ

0 голосов
/ 02 июля 2018

Этот индекс «покрытия» может помочь некоторым:

visits:  (vis_doctor, vis_report, vis_location)

Вероятно, было бы лучше удалить ненужные предложения, например, содержащие LIKE '%'. С этим связано

          AND  (locations.loc_province IS NULL
              OR  locations.loc_province LIKE '%')

, вероятно, очень плохо оптимизирован. План A: Удалить это. План Б: избегайте использования NULL для чего бы то ни было.

Наличие JOIN и GROUP BY поднимает красный флаг. Добавление DISTINCT просто «неправильно». Пожалуйста, решите, нужен ли вам GROUP BY или DISTINCT. Я не думаю, что вам нужно. Конечно, не используйте оба.

Что ??

 ( SELECT COUNT(*) ... ) AS vis_count_user
 HAVING vis_count_user <> -1

HAVING ничего не делает, кроме как замедляет запрос.

AND  DATE(doctors.doc_timestamp) <= '2018-07-02'

Скрытие столбца в функции предотвращает использование индекса. Кстати, каковы показатели? Пожалуйста, предоставьте SHOW CREATE TABLE. Это имеет ту же семантику:

AND  doctors.doc_timestamp < '2018-07-02' + INTERVAL 1 DAY

Вы действительно хотели иметь 6 месяцев плюс 1 (или 2?) День:

AND reports.rep_dated >= '2018-01-01' 
AND reports.rep_dated <= '2018-07-02'

Рассмотрим

AND reports.rep_dated >= '2018-01-01' 
AND reports.rep_dated  < '2018-01-01' + INTERVAL 6 MONTH
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...