Оптимизация соединения MySQL: улучшение типа соединения с помощью производных таблиц и GROUP BY - PullRequest
1 голос
/ 21 июня 2011

Я пытаюсь улучшить запрос, который выполняет следующие действия:

Для каждой работы сложите все расходы, сложите сумму счета и рассчитайте прибыль / убыток.Расходы поступают из нескольких разных таблиц, например, заказчики, пользователи (события, выделенные инженером на время, потраченное на сайте), использованные запасы и т. Д.
В запросе также должны быть выведены некоторые другие столбцы, такие как название сайта для работы,так что этот столбец можно отсортировать по (после всего этого добавляется ORDER BY).

SELECT
    jobs.job_id,
    jobs.start_date,
    jobs.end_date,
    events.time,
    sites.name site,
    IFNULL(stock_cost,0) stock_cost,
    labour,
    materials,
    labour+materials+plant+expenses revenue,
    (labour+materials+plant)-(time*3557/360000+IFNULL(orders_cost,0)+IFNULL(stock_cost,0)) profit,
    ((labour+materials+plant)-(time*3557/360000+IFNULL(orders_cost,0)+IFNULL(stock_cost,0)))/(time*3557/360000+IFNULL(orders_cost,0)+IFNULL(stock_cost,0)) ratio

FROM
    jobs

    LEFT JOIN (
        SELECT
            job_id,
            SUM(labour_charge) labour,
            SUM(materials_charge) materials,
            SUM(plant_hire_charge) plant,
            SUM(expenses) expenses
        FROM invoices
        GROUP BY job_id
        ORDER BY NULL
    ) invoices USING(job_id)

    LEFT JOIN (
        SELECT
            job_id,
            SUM(IF(start_onsite && end_onsite,end_onsite-start_onsite,end-start)) time,
            SUM(travel+parking+materials) user_expenses
        FROM users_events
        WHERE type='job'
        GROUP BY job_id
        ORDER BY NULL
    ) events USING(job_id)

    LEFT JOIN (
        SELECT
            job_id,
            SUM(IFNULL(total,0))*0.01 orders_cost
        FROM purchaseorders
        GROUP BY job_id
        ORDER BY NULL
    ) purchaseorders USING(job_id)

    LEFT JOIN (
        SELECT
            location job_id,
            SUM(amount*cost))*0.01 stock_cost
        FROM stock_location
        LEFT JOIN stock_items ON stock_items.id=stock_location.stock_id
        WHERE location>=3000 AND amount>0 AND cost>0
        GROUP BY location
        ORDER BY NULL
    ) stock USING(job_id)

    LEFT JOIN contacts_sites sites ON sites.id=jobs.site_id;

Я прочитал это: http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html, но не вижу, как / если я могу применитьчто-нибудь в этом.В целях тестирования я попытался добавить всевозможные индексы в полях слева, справа и в центре без каких-либо улучшений в вывод EXPLAIN:

+----+-------------+----------------+--------+------------------------+---------+---------+------------------------------------+-------+-------------------------------+
| id | select_type | table          | type   | possible_keys          | key     | key_len | ref                                | rows  | Extra                         |
+----+-------------+----------------+--------+------------------------+---------+---------+------------------------------------+-------+-------------------------------+
|  1 | PRIMARY     | jobs           | ALL    | NULL                   | NULL    | NULL    | NULL                               |  7088 |                               |
|  1 | PRIMARY     | <derived2>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |  5038 |                               |
|  1 | PRIMARY     | <derived3>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |  6476 |                               |
|  1 | PRIMARY     | <derived4>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |   904 |                               |
|  1 | PRIMARY     | <derived5>     | ALL    | NULL                   | NULL    | NULL    | NULL                               |   531 |                               |
|  1 | PRIMARY     | sites          | eq_ref | PRIMARY                | PRIMARY | 4       | bestbee_db.jobs.site_id            |     1 |                               |
|  5 | DERIVED     | stock_location | ALL    | stock,location,amount,…| NULL    | NULL    | NULL                               |  5426 | Using where; Using temporary; |
|  5 | DERIVED     | stock_items    | eq_ref | PRIMARY                | PRIMARY | 4       | bestbee_db.stock_location.stock_id |     1 | Using where                   |
|  4 | DERIVED     | purchaseorders | ALL    | NULL                   | NULL    | NULL    | NULL                               |  1445 | Using temporary;              |
|  3 | DERIVED     | users_events   | ALL    | type,type_job          | NULL    | NULL    | NULL                               | 11295 | Using where; Using temporary; |
|  2 | DERIVED     | invoices       | ALL    | NULL                   | NULL    | NULL    | NULL                               |  5320 | Using temporary;              |
+----+-------------+----------------+--------+------------------------+---------+---------+------------------------------------+-------+-------------------------------+

Получено строк 5 x 10 ^ 21 (вместо 3 x10 ^ 42 до того, как я начал оптимизировать этот запрос!)
В настоящее время выполнение занимает семь секунд (вместо 26), но я бы хотел, чтобы это было менее одной секунды.

Кстати: GROUP BY xORDER BY NULL - отличный способ удалить ненужные сортировки файлов из подзапросов!(от http://www.mysqlperformanceblog.com/2006/09/04/group_concat-useful-group-by-extension/)

1 Ответ

0 голосов
/ 22 июня 2011

Исходя из вашего комментария к моему вопросу, я бы сделал следующее ...

На самом верху ...

ВЫБРАТЬ STRAIGHT_JOIN (просто добавьте ключевое слово "STRAIGH_JOIN")

Затем для каждого из ваших подзапросов по счетам, событиям, операциям и т. Д. Замените ORDER BY на JOB_ID в явном виде, чтобы это могло помочь в оптимизации при объединении с первичной таблицей JOBS.

Наконец, убедитесь, что каждая из ваших таблиц подзапросов ИМЕЕТ индекс для Job_ID (Счета-фактуры, User_events, PurchaseOrders, Stock_Location)

Кроме того, для таблицы Stock_Location вы можете захотеть помочь предложению WHERE для своего подзапроса, указав составной индекс на
(job_id, location, amount) Трех полей по глубине должно быть достаточно, даже если у вас есть ключ плюс 3, где элементы условия.

...