Mysql OneToMany JOIN только самая последняя запись

У меня есть две таблицы customers и их contacts. У клиента может быть много контактных данных. В случае, если мне нужно получить только последние добавленные контактные данные для клиентов. Я могу получить это subquery. Но когда данные огромны, я сталкиваюсь с недостатком производительности при запросе всех данных клиентов.

Таблица клиентов (customers_customers)

+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | int(11)      | NO   | PRI | NULL    | auto_increment |
| company_name      | varchar(150) | NO   |     | NULL    |                |
| logo              | varchar(100) | NO   |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+

Таблица контактов (customers_customercontacts)

+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| email       | varchar(100) | YES  |     | NULL    |                |
| mobile      | varchar(50)  | YES  |     | NULL    |                |
| customer_id | int(11)      | NO   | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

Я пробовал со следующими запросами, я получаю результаты, но запрос работает медленно.

SELECT
    c.id,
    c.company_name,
    d.mobile
FROM customers_customers AS c
LEFT JOIN customers_customercontacts AS d
    ON d.id = (SELECT MAX(id) FROM customers_customercontacts WHERE d.customer_id = d.id);

и

SELECT
    c.id,
    c.company_name,
    d.mobile
FROM customers_customers AS c
LEFT JOIN customers_customercontacts AS d
    ON d.id = (SELECT id FROM customers_customercontacts WHERE d.customer_id = d.id
               ORDER BY id DESC LIMIT 1);

Мне нужно получить имя компании клиента и последний добавленный номер телефона каждого имени компании. Есть ли какой-либо оптимизированный способ или способ без использования подзапроса для достижения этого?

Решено

Некоррелированные подзапросы всегда обеспечивают более высокую производительность, чем коррелированные подзапросы.


person Rajez    schedule 10.11.2017    source источник
comment
Вы можете объяснить свой запрос?   -  person rai    schedule 10.11.2017
comment
Мне нужно получить названия компаний-клиентов и их номера телефонов. Только один номер телефона для каждого клиента, и он должен быть добавлен последним.   -  person Rajez    schedule 10.11.2017
comment
Ответ Билла Карвина может помочь вам Retrieving the last record in each group   -  person M Khalid Junaid    schedule 10.11.2017
comment
@Rajez Нет, я имею в виду, используйте синтаксис объяснения, таблица может быть не оптимизирована, и где-то должен быть индекс   -  person rai    schedule 10.11.2017
comment
Оптимальный максимум для группы   -  person Rick James    schedule 11.11.2017


Ответы (1)


Используйте второе соединение с подзапросом, который идентифицирует самую последнюю запись контакта для каждого клиента:

SELECT
    c.id,
    c.company_name,
    d1.mobile
FROM customers_customers AS c
LEFT JOIN customers_customercontacts AS d1
    ON c.id = d1.customer_id
INNER JOIN
(
    SELECT customer_id, MAX(id) AS max_id
    FROM customers_customercontacts
    GROUP BY customer_id
) AS d2
    ON d1.customer_id = d2.customer_id AND
       d1.id = d2.max_id;

Это модификация вашей первой попытки запроса. Обратите внимание, что ваше условие соединения было нарушено, потому что оно на самом деле не связывает должным образом таблицу клиентов и контактов. Базовое соединение должно быть customers_customers.id соответствует customers_customercontacts.customer_id. Вдобавок к этому исправлению я делаю дополнительное объединение, чтобы ограничиться самой последней записью контакта для каждого клиента.

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

person Tim Biegeleisen    schedule 10.11.2017
comment
Спасибо. Я вижу хорошее улучшение продолжительности запроса. Можете ли вы объяснить, как это изменение улучшает производительность? - person Rajez; 10.11.2017
comment
@Rajez Я добавил объяснение. - person Tim Biegeleisen; 11.11.2017