Вопрос

Я хочу иметь возможность выбрать несколько строк из таблицы электронных писем и сгруппировать их по отправителю from.Мой запрос выглядит примерно так:

SELECT 
    `timestamp`, `fromEmail`, `subject`
FROM `incomingEmails` 
GROUP BY LOWER(`fromEmail`) 
ORDER BY `timestamp` DESC

Запрос работает почти так, как я хочу — он выбирает записи, сгруппированные по электронной почте.Проблема в том, что тема и временная метка не соответствуют самой последней записи для конкретного адреса электронной почты.

Например, он может возвращать:

fromEmail: john@example.com, subject: hello
fromEmail: mark@example.com, subject: welcome

Когда записи в базе данных являются:

fromEmail: john@example.com, subject: hello
fromEmail: john@example.com, subject: programming question
fromEmail: mark@example.com, subject: welcome

Если тема "вопрос по программированию" является самой последней, как я могу заставить MySQL выбирать эту запись при группировании электронных писем?

Это было полезно?

Решение

Простое решение состоит в том, чтобы обернуть запрос в подвыборку с инструкцией ORDER Первый и применяя ГРУППУ ПО позже:

SELECT * FROM ( 
    SELECT `timestamp`, `fromEmail`, `subject`
    FROM `incomingEmails` 
    ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)

Это похоже на использование join, но выглядит намного приятнее.

Использование неагрегированных столбцов в предложении SELECT с предложением GROUP BY является нестандартным.MySQL обычно возвращает значения первой найденной строки и отбрасывает остальные.Любые предложения ORDER BY будут применяться только к возвращаемому значению столбца, а не к отброшенным.

ВАЖНОЕ ОБНОВЛЕНИЕ Выбор неагрегированных столбцов используется для работы на практике, но на него не следует полагаться.В соответствии с Документация MySQL "это полезно в первую очередь, когда все значения в каждом неагрегированном столбце, не названном в GROUP BY, одинаковы для каждой группы.Сервер находится свободно выбирать любое значение от каждой группы, так что если они не совпадают, выбранные значения являются неопределенными."

Начиная с версии 5.6.21, я заметил проблемы с GROUP 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/en/group-by-handling.html

Другие советы

Вот один из подходов:

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 вы указываете, что более поздних строк быть не может.Это дает вам только последнюю строку.

Если может быть несколько электронных писем с одной и той же временной меткой, этот запрос потребует уточнения.Если в таблице электронной почты есть столбец с добавочным идентификатором, измените соединение следующим образом:

LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.id < next.id

Выполните GROUP BY после ORDER BY, обернув ваш запрос GROUP BY следующим образом:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from

Как уже указывалось в ответе, текущий ответ неверен, потому что 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(), который в большинстве случаев использоваться не будет.

Согласно стандарту SQL, вы не можете использовать неагрегированные столбцы в списке выбора.MySQL допускает такое использование (без использования режима ONLY_FULL_GROUP_BY), но результат непредсказуем.

ONLY_FULL_GROUP_BY ЕДИНСТВЕННАЯ_ГРУППА_BY

Сначала вы должны выбрать fromEmail, MIN(читать), а затем, с помощью второго запроса (или подзапроса) - Subject.

Я боролся с обоими этими подходами для более сложных запросов, чем те, что показаны, потому что подход с подзапросами был ужасно неэффективным, независимо от того, какие индексы я ввел, и потому что я не мог получить внешнее самосоединение через Hibernate

Лучший (и самый простой) способ сделать это - сгруппировать по чему-либо, что сконструировано так, чтобы содержать конкатенацию требуемых вам полей, а затем извлечь их, используя выражения в предложении SELECT .Если вам нужно выполнить функцию MAX(), убедитесь, что поле, над которым вы хотите выполнить функцию MAX(), всегда находится в самом значимом конце объединенного объекта.

Ключом к пониманию этого является то, что запрос может иметь смысл только в том случае, если эти другие поля инвариантны для любого объекта, который удовлетворяет Max() , поэтому с точки зрения сортировки другие части конкатенации могут быть проигнорированы.В самом низу этой ссылки объясняется, как это сделать. http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html

Если вы можете получить событие insert / update (например, триггер) для предварительного вычисления конкатенации полей, вы можете проиндексировать его, и запрос будет таким же быстрым, как если бы group by была только над полем, которое вы на самом деле хотели MAX().Вы даже можете использовать его, чтобы получить максимум из нескольких полей.Я использую его для выполнения запросов к многомерным деревьям, выраженным как вложенные наборы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top