определить строку, возвращаемую при группировке - PullRequest
0 голосов
/ 21 апреля 2020

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

  • tbl_contact имеет поля: id, name
  • tbl_phone имеет поля: id, contact_id, phone, primary

tbl_phone.contact_id - это внешний ключ для tbl_contact.id. primary - логический флаг, указывающий, какая из нескольких телефонных записей является первичной записью контакта.

Основная цель c - запросить все телефонные записи, но всегда возвращать первичную запись, если не указано c номер телефона запрашивается. Допустим, у контакта есть две телефонные записи, одна с номером «1234» и одна с номером «5678», и, скажем, последний помечен как основной. Если пользователь ищет телефон =% 1234% - нет проблем, мы возвращаем эту строку. Но если мы ищем имя контакта без фильтра телефона, оно всегда должно возвращать «5678» - первичную запись.

Самый близкий из всех, что я получил, - это подзапрос:

SELECT *
FROM (
  SELECT c.id contact_id, c.name, p.phone, p.primary
  FROM tbl_contact c
  LEFT JOIN tbl_phone p
    ON c.id = p.contact_id
  WHERE [possibly searching name or phone]
  ORDER BY p.primary DESC
)
GROUP BY contact_id

Таким образом, по сути, внутренний подзапрос получает все контакты + телефон, отсортированные по основному DES C - так, чтобы те, отмеченные 1, были бы перечислены первыми. Внешний запрос и группа выбирают одну запись для каждого контакта. Да, я знаю, что GROUP BY нарушает правило режима ONLY_FULL_GROUP_BY по умолчанию, но давайте просто go с ним ради обсуждения.

Как отмечено в другом месте, например: Какие поля строки возвращаются при группировании с MySQL? ... документация MySQL указывает, что GROUP BY будет возвращать строки случайным образом. НО ... большинство людей считают, что вышеупомянутое работает успешно, потому что обычно GROUP BY возвращает первую строку внутреннего подзапроса.

Однако в моей ситуации это не возвращает грести надежно И, судя по всему, я не могу придумать альтернативное решение. Пара дополнительных примечаний:

  • Мне нужно иметь возможность искать все номера телефонов для контакта, поэтому я не могу применить фильтры предложений WHERE для первичного = 1 к внутреннему запросу.
  • Я не могу применить фильтр предложения WHERE первичного = 1 к внешнему запросу, потому что, если они искали неосновной телефон, в результатах не будет основной телефонной записи для этого контакта.
  • Производительность важна, но я хотел бы, по крайней мере, придумать что-то, что действительно работает ...

ОБНОВЛЕНИЕ: Мы работаем MySQL 5.7

1 Ответ

0 голосов
/ 21 апреля 2020

Если вы ищете конкретный c номер телефона, тогда ваш SQL будет просто:

SELECT c.id, p.contact_id, c.name, p.phone, p.`primary`
  FROM tbl_contact c join tbl_phone p on c.id = p.contact_id
  WHERE phone = '212-555-1112'

См. БД Fiddle

Другой дело, где вы хотите получить первичные телефонные номера для всех или одного указанного c имени. Рассмотрим следующий подзапрос:

SELECT id, contact_id, phone, `primary`
FROM tbl_phone
WHERE `primary`

. Выбирает все основные телефонные номера из tbl_phone. Тогда наш запрос будет выглядеть следующим образом:

SELECT c.id, p.contact_id, c.name, p.phone, p.`primary`
FROM tbl_contact c
LEFT JOIN
(
    SELECT id, contact_id, phone, `primary`
    FROM tbl_phone
    WHERE `primary`
) p on c.id = p.contact_id
WHERE name = 'Jane Doe' /* remove this WHERE CLAUSE to get all orimary phone numbers */

Удалите предложение WHERE в приведенном выше SQL, чтобы получить основные телефонные номера для всех контактов.

См. DB Fiddle

Но почему бы просто:

SELECT c.id, p.contact_id, c.name, p.phone, p.`primary`
FROM tbl_contact c left join tbl_phone p
ON c.id = p.contact_id
WHERE /* condition */

Примеры предложения WHERE:

WHERE `primary` /* get all primary phone numbers */
WHERE name = 'Jane Doe' /* get all phone numbers for Jane Doe */
WHERE phone = '212-555-1212' /* get specific phone number */
WHERE name = 'Jane Doe' AND `primary` /* get Jane Doe's primary phone number */

Для пунктов WHERE 1 , 3 и 4 вы также можете выполнять ВНУТРЕННЕЕ СОЕДИНЕНИЕ (фактически, нет смысла делать ВНЕШНЕЕ СОЕДИНЕНИЕ, если не будет случая контакта без хотя бы одного основного номера телефона).

I думаю, вы сделали это более сложным, чем на самом деле.

DB Fiddle

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