MySQL "Group By" и "Order By"
Я хочу иметь возможность выбирать кучу строк из таблицы электронных писем и группировать их с отправителя. Мой запрос выглядит так:
SELECT
`timestamp`, `fromEmail`, `subject`
FROM `incomingEmails`
GROUP BY LOWER(`fromEmail`)
ORDER BY `timestamp` DESC
Запрос почти работает так, как я хочу - он выбирает записи, сгруппированные по электронной почте. Проблема в том, что тема и временная метка не соответствуют самой последней записи для определенного адреса электронной почты.
Например, он может вернуться:
fromEmail: [email protected], subject: hello
fromEmail: [email protected], subject: welcome
Когда в базе данных есть записи:
fromEmail: [email protected], subject: hello
fromEmail: [email protected], subject: programming question
fromEmail: [email protected], subject: welcome
Если тема "вопрос программирования" является самой последней, как я могу заставить MySQL выбрать эту запись при группировке сообщений электронной почты?
Ответы
Ответ 1
Простое решение состоит в том, чтобы сначала обернуть запрос в подвыбор с помощью оператора ORDER, а затем применить GROUP BY:
SELECT * FROM (
SELECT 'timestamp', 'fromEmail', 'subject'
FROM 'incomingEmails'
ORDER BY 'timestamp' DESC
) AS tmp_table GROUP BY LOWER('fromEmail')
Это похоже на использование соединения, но выглядит намного лучше.
Использование неагрегированных столбцов в SELECT с предложением GROUP BY является нестандартным. MySQL обычно возвращает значения первой найденной строки и отбрасывает остальные. Любые предложения ORDER BY будут применяться только к возвращенному значению столбца, а не к отброшенным.
ВАЖНОЕ ОБНОВЛЕНИЕ Выбор неагрегированных столбцов, используемых для практической работы, но на них не следует полагаться. Согласно документации MySQL "это полезно, прежде всего, когда все значения в каждом неагрегированном столбце, не названном в GROUP BY, одинаковы для каждой группы. Сервер может выбрать любое значение из каждой группы, поэтому, если они не совпадают, значения Избранные не определены.
По состоянию на 5.6.21 я заметил проблемы с GROUP BY во временной таблице, возвращающие сортировку ORDER BY.
По состоянию на 5.7.5 ONLY_FULL_GROUP_BY включен по умолчанию, т.е. невозможно использовать неагрегированные столбцы.
См. Http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html https://dev.mysql.com/DOC/RefMan/5.7/ен/группы по-handling.html
Ответ 2
Здесь один подход:
SELECT cur.textID, cur.fromEmail, cur.subject,
cur.timestamp, cur.read
FROM incomingEmails cur
LEFT JOIN incomingEmails next
on cur.fromEmail = next.fromEmail
and cur.timestamp < next.timestamp
WHERE next.timestamp is null
and cur.toUserID = '$userID'
ORDER BY LOWER(cur.fromEmail)
В принципе, вы присоединяетесь к таблице самостоятельно, ища более поздние строки. В предложении where вы указываете, что последующих строк не может быть. Это дает вам только последнюю строку.
Если может быть несколько писем с одинаковой меткой времени, этот запрос нуждается в уточнении. Если в таблице электронной почты есть столбец с возрастающим идентификатором, измените JOIN следующим образом:
LEFT JOIN incomingEmails next
on cur.fromEmail = next.fromEmail
and cur.id < next.id
Ответ 3
Сделайте GROUP BY после ORDER BY, обернув свой запрос с помощью GROUP BY следующим образом:
SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from
Ответ 4
Как уже указывалось в ответе, текущий ответ неверен, поскольку GROUP BY произвольно выбирает запись из окна.
Если вы используете MySQL 5.6 или MySQL 5.7 с ONLY_FULL_GROUP_BY
, правильный (детерминированный) запрос:
SELECT incomingEmails.*
FROM (
SELECT fromEmail, MAX(timestamp) `timestamp`
FROM incomingEmails
GROUP BY fromEmail
) filtered_incomingEmails
JOIN incomingEmails USING (fromEmail, timestamp)
GROUP BY fromEmail, timestamp
Чтобы запрос работал эффективно, требуется правильная индексация.
Обратите внимание, что для упрощения я удалил LOWER()
, который в большинстве случаев не будет использоваться.
Ответ 5
В соответствии со стандартом SQL вы не можете использовать неагрегатные столбцы в списке выбора.
MySQL допускает такое использование (используется режим uless ONLY_FULL_GROUP_BY), но результат не предсказуем.
ONLY_FULL_GROUP_BY
Сначала вы должны выбрать из Email, MIN (read), а затем со вторым запросом (или подзапросом) - Subject.
Ответ 6
Я боролся с обоими этими подходами для более сложных запросов, чем те, которые были показаны, потому что подзапрос был ужасно неопределен, независимо от того, какие индексы я надел, и потому, что я не мог получить внешнее самосоединение через Hibernate
Лучший (и самый простой) способ сделать это состоит в том, чтобы сгруппировать что-то, что создано, чтобы содержать конкатенацию требуемых полей, а затем вытаскивать их с помощью выражений в предложении SELECT. Если вам нужно выполнить MAX(), убедитесь, что поле, которое вы хотите MAX(), всегда находится на самом значительном конце конкатенированного объекта.
Ключом к пониманию этого является то, что запрос может иметь смысл только в том случае, если эти другие поля являются инвариантными для любого объекта, который удовлетворяет Max(), поэтому с точки зрения сортировки другие части конкатенации можно игнорировать. В нем объясняется, как это сделать в самом низу этой ссылки. http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html
Если вы можете получить событие insert/update (например, триггер), чтобы предварительно вычислить конкатенацию полей, которые вы можете проиндексировать, и запрос будет таким же быстрым, как если бы группа была по той же причине, которую вы действительно хотели до MAX(). Вы можете даже использовать его для получения максимального количества полей. Я использую его для выполнения запросов к многомерным деревьям, выраженным как вложенные наборы.