У меня проблема с медленным запросом MySQL (MySQL 5+). Давайте подумаем из трех таблиц:Медленные внутренние соединения с подзапросами в MySQL
customers:
- id_customer : int (PRIMARY)
- name : varchar(255)
customers_addresses:
- id_customers_addresses : int (PRIMARY)
- id_customer : int (INDEX)
- street : varchar(255)
- zipcode : varchar(255)
- city : varchar(255)
customers_contacts:
- id_customers_contacts : int (PRIMARY)
- id_customer : int (INDEX)
- type : varchar(255)
- value : varchar(255)
Теперь моя цель состоит в том, чтобы собрать все адреса и контактную информацию в одном запросе и с одной строки на каждого клиента. Моя первая попытка была с помощью LEFT JOIN
с, как некоторые клиенты не имеют никакого адреса и/или контактную информацию:
SELECT customers.id_customer,
customers.name,
X.contact AS contact,
Y.street,
Y.zipcode,
Y.city
FROM customers
LEFT JOIN
(
SELECT
GROUP_CONCAT(CONCAT(type, ': ', value) SEPARATOR ', ') AS contact,
id_customer
FROM customers_contacts
GROUP BY id_customer
) AS X
ON X.id_customer = customers.id_customer
LEFT JOIN
(
SELECT
GROUP_CONCAT(street SEPARATOR '<br>') AS street,
GROUP_CONCAT(zipcode SEPARATOR '<br>') AS zipcode,
GROUP_CONCAT(city SEPARATOR '<br>') AS city,
id_customer
FROM customers_addresses
GROUP BY id_customer
) AS Y
ON Y.id_customer = customers.id_customer
WHERE Y.street LIKE '%Avenue%'
ORDER BY customers.name DESC
LIMIT 0, 20
Этот запрос потребовался более 130 секунд, чтобы завершить (за ~ 7000 записей в каждой таблице), что далеко из хорошо.
Предварение EXPLAIN EXTENDED
дает:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY customers ref name name 3 const 4334 100.00 Using where; Using temporary; Using filesort
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 7793 100.00
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 8580 100.00 Using where
3 DERIVED customers_addresses index NULL id_customer 5 NULL 8651 100.00
2 DERIVED customers_contacts index NULL id_customer 4 NULL 9314 100.00
Я прочитал некоторые сообщения StackOverflow и документации MySQL. Оба говорят, что INNER JOIN
намного быстрее. Я пытался повторить LEFT JOIN
поведение с INNER JOIN
с помощью UNION ALL
:
SELECT customers.id_customer,
customers.name,
X.contact AS contact,
Y.street,
Y.zipcode,
Y.city
FROM customers
INNER JOIN
(
SELECT
GROUP_CONCAT(CONCAT(type, ': ', value) SEPARATOR ', ') AS contact,
id_customer
FROM customers_contacts
GROUP BY id_customer
UNION ALL
SELECT
'' AS contact,
id_customer
FROM customers
WHERE id_customer NOT IN (SELECT DISTINCT id_customer FROM customers_contacts)
) AS X
ON X.id_customer = customers.id_customer
INNER JOIN
(
SELECT
GROUP_CONCAT(street SEPARATOR '<br>') AS street,
GROUP_CONCAT(zipcode SEPARATOR '<br>') AS zipcode,
GROUP_CONCAT(city SEPARATOR '<br>') AS city,
id_customer
FROM customers_addresses
GROUP BY id_customer
UNION ALL
SELECT
'' AS street,
'' AS zipcode,
'' AS city,
id_customer
FROM customers
WHERE id_customer NOT IN (SELECT DISTINCT id_customer FROM customers_addresses)
) AS Y
ON Y.id_customer = customers.id_customer
WHERE Y.street LIKE '%Avenue%'
ORDER BY customers.name DESC
LIMIT 0, 20
Этот запрос улучшена производительность на 20 секунд. Но 110 секунд все еще неприемлемы.
Предварение EXPLAIN EXTENDED
:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 8596 100.00 Using temporary; Using filesort
1 PRIMARY <derived5> ALL NULL NULL NULL NULL 8604 100.00 Using join buffer
1 PRIMARY customers eq_ref PRIMARY,name,name3 PRIMARY 4 Y.id_kunde 1 100.00 Using where
5 DERIVED customers_addresses index NULL id_kunde 5 NULL 8651 100.00
6 UNION customers index NULL name2 767 NULL 8677 100.00 Using where; Using index
7 DEPENDENT SUBQUERY customers_addresses index_subquery id_kunde id_kunde 5 func 2 100.00 Using index
NULL UNION RESULT <union5,6> ALL NULL NULL NULL NULL NULL NULL
2 DERIVED customers_contacts index NULL id_kunde 4 NULL 10411 100.00
3 UNION customers index NULL name2 767 NULL 8677 100.00 Using where; Using index
4 DEPENDENT SUBQUERY customers_contacts index_subquery id_kunde id_kunde 4 func 1 100.00 Using index
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Так вот мой вопрос: Как улучшить один из этих запросов и/или таблиц базы данных, чтобы получить супер быстрый ответ? Я не только заинтересован в решении, но и в стратегии, как предотвратить такую производительность в будущем.
С уважением.
«Этот запрос занял более 130 секунд, чтобы завершить« Er, no. Этот запрос является синтаксически неправильным. – Strawberry
Извините, я упустил часть 'WHERE', пока убирал запрос на переполнение стека. должно быть правильным (проверено) – Timm
Почему 'LEFT JOIN' на' customers_addresses', когда он должен иметь улицу 'LIKE '% Avenue%''? –