Есть много подобных вопросов, которые можно найти здесь, но я не думаю, что кто-либо ответит на вопрос адекватно.
Я продолжу от текущего самого популярного вопроса и буду использовать их пример, если это хорошо.
Задача в этом случае - получить последний пост для каждого автора в базе данных.
Пример запроса дает непригодные результаты, так как он не всегда является последним возвращаемым сообщением.
SELECT wp_posts.* FROM wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
ORDER BY wp_posts.post_date DESC
Текущий принятый ответ
SELECT
wp_posts.*
FROM wp_posts
WHERE
wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC
К сожалению, этот ответ прост и неверен и во многих случаях дает менее стабильные результаты, чем оригинальный запрос.
Мое лучшее решение - использовать подзапрос вида
SELECT wp_posts.* FROM
(
SELECT *
FROM wp_posts
ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
Тогда у меня простой вопрос: есть ли в любом случае порядок строк перед группировкой, не прибегая к подзапросу?
Изменить : Этот вопрос был продолжением другого вопроса, и специфика моей ситуации немного отличается. Вы можете (и должны) предположить, что существует также wp_posts.id, который является уникальным идентификатором для этого конкретного сообщения.
источник
post_author
иpost_date
не достаточно, чтобы получить уникальный ряд, поэтому должно быть больше, чтобы получить уникальный ряд дляpost_author
There are plenty of similar questions to be found on here but I don't think that any answer the question adequately.
Для этого и есть награды.Ответы:
Использование
ORDER BY
в подзапросе не лучшее решение этой проблемы.Лучшее решение для получения
max(post_date)
автора - использовать подзапрос, чтобы вернуть максимальную дату, а затем присоединить ее к вашей таблице на обоихpost_author
на максимальную, и на максимальную дату.Решение должно быть:
Если у вас есть следующие образцы данных:
Подзапрос вернет максимальную дату и автора:
Затем, поскольку вы присоединяете это обратно к таблице, для обоих значений вы вернете полную информацию об этом посте.
Смотрите SQL Fiddle с демонстрацией .
Чтобы расширить мои комментарии об использовании подзапроса для точного возврата этих данных.
MySQL не принуждает вас к
GROUP BY
каждому столбцу, который вы включаете вSELECT
список. В результате, если вы толькоGROUP BY
один столбец, но возвращаете в общей сложности 10 столбцов, нет никакой гарантии, что значения других столбцов, которые принадлежатpost_author
, возвращаются. Если столбец отсутствует вGROUP BY
MySQL, выбирает, какое значение следует вернуть.Использование подзапроса с функцией агрегата гарантирует, что каждый раз будет возвращаться правильный автор и сообщение.
В качестве примечания, хотя MySQL позволяет вам использовать
ORDER BY
в подзапросе и применятьGROUP BY
к не каждому столбцу вSELECT
списке, это поведение не разрешено в других базах данных, включая SQL Server.источник
wp_posts
в обоих столбцах, чтобы получить полную строку.GROUP BY
только к одному столбцу, нет гарантии, что значения в других столбцах будут последовательно правильными. К сожалению, MySQL допускает такой тип SELECT / GROUPing, что другие продукты этого не делают. Во-вторых, синтаксис использованияORDER BY
в подзапросе, хотя и разрешен в MySQL, не разрешен в других продуктах баз данных, включая SQL Server. Вы должны использовать решение, которое будет возвращать правильный результат при каждом его выполнении.INDEX(post_author, post_date)
важно соединение .post_id
свой внутренний запрос, то технически вы должны также сгруппировать его, что, скорее всего, исказит ваши результаты.Ваше решение использует расширение предложения GROUP BY , которое позволяет группировать по некоторым полям (в данном случае просто
post_author
):и выберите неагрегированные столбцы:
которые не перечислены в предложении group by или которые не используются в статистической функции (MIN, MAX, COUNT и т. д.).
Правильное использование расширения для предложения GROUP BY
Это полезно, когда все значения неагрегированных столбцов равны для каждой строки.
Например, предположим, у вас есть стол
GardensFlowers
(name
сада,flower
который растет в саду):и вы хотите извлечь все цветы, которые растут в саду, где растут несколько цветов. Затем вы должны использовать подзапрос, например, вы можете использовать это:
Если вам нужно извлечь все цветы, которые являются единственными цветами в гардере, вы можете просто изменить условие HAVING на
HAVING COUNT(DISTINCT flower)=1
, но MySql также позволяет вам использовать это:нет подзапроса, не стандартный SQL, но проще.
Неправильное использование расширения для предложения GROUP BY
Но что произойдет, если вы выберете неагрегированные столбцы, которые не равны для каждой строки? Какое значение выбирает MySql для этого столбца?
Похоже, что MySql всегда выбирает первое значение, с которым сталкивается.
Чтобы убедиться, что первое значение, с которым оно сталкивается, является именно тем значением, которое вы хотите, вам нужно применить a
GROUP BY
к упорядоченному запросу, следовательно, необходимо использовать подзапрос. Вы не можете сделать это иначе.Учитывая предположение, что MySql всегда выбирает первую встреченную строку, вы корректно сортируете строки перед GROUP BY. Но, к сожалению, если вы внимательно прочитаете документацию, вы заметите, что это предположение неверно.
При выборе неагрегированных столбцов, которые не всегда одинаковы, MySql может выбрать любое значение, поэтому результирующее значение, которое он фактически показывает, является неопределенным .
Я вижу, что этот прием для получения первого значения неагрегированного столбца часто используется, и он обычно / почти всегда работает, иногда я тоже его использую (на свой страх и риск). Но поскольку это не задокументировано, вы не можете полагаться на это поведение.
Эта ссылка (спасибо ypercube!), Трюк GROUP BY был оптимизирован, показывает ситуацию, в которой один и тот же запрос возвращает разные результаты между MySql и MariaDB, возможно, из-за другого механизма оптимизации.
Так что, если этот трюк сработает, это просто вопрос удачи.
Общепринятый ответ на другой вопрос выглядит не так со мной:
wp_posts.post_date
является неагрегированным столбцом, и его значение будет официально не определено, но, скорее всего, оно будет первымpost_date
. Но поскольку трюк GROUP BY применяется к неупорядоченной таблице, он не уверен, какой из нихpost_date
встречается первым .Вероятно, он вернет сообщения, которые являются единственными публикациями одного автора, но даже это не всегда точно.
Возможное решение
Я думаю, что это может быть возможным решением:
На внутренний запрос я возвращаю максимальную дату публикации для каждого автора. Затем я принимаю во внимание тот факт, что один и тот же автор теоретически может иметь две записи одновременно, поэтому я получаю только максимальный идентификатор. А потом я возвращаю все строки, которые имеют эти максимальные идентификаторы. Это можно сделать быстрее, используя соединения вместо предложения IN.
(Если вы уверены, что
ID
это только увеличивается, и если этоID1 > ID2
также означаетpost_date1 > post_date2
, что запрос можно сделать намного проще, но я не уверен, что это так).источник
extension to GROUP By
интересное чтение, спасибо за это.То, что вы собираетесь прочитать, довольно хакерское, так что не пытайтесь делать это дома!
В целом, в SQL ответом на ваш вопрос является НЕТ , но из-за смягченного режима
GROUP BY
(упомянутого @bluefeet ) ответ - ДА в MySQL.Предположим, у вас есть индекс BTREE (post_status, post_type, post_author, post_date). Как выглядит индекс под капотом?
(post_status = 'publish', post_type = 'post', post_author = 'user A', post_date = '2012-12-01') (post_status = 'publish', post_type = 'post', post_author = 'user A', post_date = '2012-12-31') (post_status = 'publish', post_type = 'post', post_author = 'user B', post_date = '2012-10-01') (post_status = 'publish', post_type = ' post ', post_author =' пользователь B ', post_date =' 2012-12-01 ')
То есть данные сортируются по всем этим полям в порядке возрастания.
Когда вы делаете
GROUP BY
по умолчанию, он сортирует данные по полю группировки (post_author
в нашем случае; post_status, post_type требуютсяWHERE
предложением), и если есть соответствующий индекс, он берет данные для каждой первой записи в порядке возрастания. То есть запрос получит следующее (первое сообщение для каждого пользователя):(post_status = 'publish', post_type = 'post', post_author = 'user A', post_date = '2012-12-01') (post_status = 'publish', post_type = 'post', post_author = 'user B', POST_DATE = '2012-10-01')
Но
GROUP BY
в MySQL позволяет указывать порядок явно. И когда вы запрашиваетеpost_user
в порядке убывания, он будет проходить через наш индекс в обратном порядке, по-прежнему принимая первую запись для каждой группы, которая фактически является последней.То есть
даст нам
(post_status = 'publish', post_type = 'post', post_author = 'user B', post_date = '2012-12-01') (post_status = 'publish', post_type = 'post', post_author = 'user A', POST_DATE = '2012-12-31')
Теперь, когда вы упорядочиваете результаты группировки по post_date, вы получаете нужные данные.
NB :
Это не то, что я бы порекомендовал для этого конкретного запроса. В этом случае я бы использовал слегка измененную версию того, что предлагает @bluefeet . Но эта техника может быть очень полезной. Посмотрите на мой ответ здесь: получение последней записи в каждой группе
Подводные камни : недостатки подхода в том, что
Преимущество - производительность в тяжелых случаях. В этом случае производительность запроса должна быть такой же, как и в запросе @ bluefeet, из-за объема данных, участвующих в сортировке (все данные загружаются во временную таблицу и затем сортируются; кстати, его запрос также требует
(post_status, post_type, post_author, post_date)
индекс) ,Что бы я предложил :
Как я уже сказал, эти запросы заставляют MySQL тратить время на сортировку потенциально огромных объемов данных во временной таблице. В случае, если вам нужно подкачки (то есть LIMIT участвует), большая часть данных даже сбрасывается. То, что я хотел бы сделать, это минимизировать количество отсортированных данных: это отсортировать и ограничить минимум данных в подзапросе, а затем присоединиться ко всей таблице.
Тот же запрос с использованием подхода, описанного выше:
Все эти запросы с их планами выполнения на SQLFiddle .
источник
Попробуй это. Просто получите список последних постов от каждого автора . Это оно
источник
post_date IN (select max(...) ...)
. Это более эффективно , чем делает группу, в суб выбора, см dev.mysql.com/doc/refman/5.6/en/subquery-optimization.htmlIN ( SELECT ... )
гораздо менее эффективен, чем эквивалентный JOIN.Нет. Нет смысла упорядочивать записи перед группировкой, так как группировка приведет к изменению результирующего набора. Способ подзапроса является предпочтительным способом. Если это происходит слишком медленно, вам придется изменить дизайн таблицы, например, сохранив идентификатор последнего сообщения для каждого автора в отдельной таблице, или ввести логический столбец, указывающий для каждого автора, какой из его сообщений является последним один.
источник
Просто используйте функцию max и функцию group
источник
Напомним, что стандартное решение использует некоррелированный подзапрос и выглядит так:
Если вы используете древнюю версию MySQL или довольно небольшой набор данных, то вы можете использовать следующий метод:
источник
** Подзапросы могут оказать плохое влияние на производительность при использовании с большими наборами данных **
Оригинальный запрос
Модифицированный запрос
потому что я использую
max
вselect clause
==>max(p.post_date)
можно избежать запросов sub select и упорядочить по столбцу max после группировки по.источник
Во-первых, не используйте * в select, это влияет на их производительность и затрудняет использование группы по порядку. Попробуйте этот запрос:
Когда вы не указываете таблицу в ORDER BY, просто псевдоним, они упорядочат результат выбора.
источник