Há muitas perguntas semelhantes a serem encontradas aqui, mas acho que nenhuma resposta é adequada.
Vou continuar com a pergunta mais popular atual e usar o exemplo deles, se estiver tudo bem.
A tarefa nesta instância é obter a última publicação de cada autor no banco de dados.
A consulta de exemplo produz resultados inutilizáveis, pois nem sempre é a postagem mais recente retornada.
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
A resposta atual aceita é
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
Infelizmente, essa resposta é pura e simplesmente errada e, em muitos casos, produz resultados menos estáveis que a consulta original.
Minha melhor solução é usar uma subconsulta do formulário
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
Minha pergunta é simples: existe alguma maneira de ordenar linhas antes de agrupar sem recorrer a uma subconsulta?
Edit : Esta questão foi uma continuação de outra questão e as especificidades da minha situação são ligeiramente diferentes. Você pode (e deve) assumir que também existe um wp_posts.id que é um identificador exclusivo para essa postagem específica.
fonte
post_author
epost_date
não são o suficiente para obter uma linha única, então tem que haver mais para obter uma linha única perpost_author
There are plenty of similar questions to be found on here but I don't think that any answer the question adequately.
É para isso que servem as recompensas.Respostas:
Usar uma
ORDER BY
em uma subconsulta não é a melhor solução para esse problema.A melhor solução para obter o
max(post_date)
autor é usar uma subconsulta para retornar a data máxima e associá-la à sua tabela napost_author
data e na data máxima.A solução deve ser:
Se você tiver os seguintes dados de amostra:
A subconsulta retornará a data máxima e o autor de:
Então, como você está juntando isso de volta à tabela, nos dois valores, você retornará os detalhes completos dessa postagem.
Veja SQL Fiddle com demonstração .
Para expandir meus comentários sobre o uso de uma subconsulta para retornar com precisão esses dados.
O MySQL não o força a
GROUP BY
todas as colunas que você inclui naSELECT
lista. Como resultado, se você apenasGROUP BY
uma coluna, mas retornar 10 colunas no total, não há garantia de que os outros valores da coluna que pertencem àpost_author
que são retornados. Se a coluna não estiver noGROUP BY
MySQL, escolha qual valor deve ser retornado.O uso da subconsulta com a função agregada garantirá que o autor e a postagem corretos sejam retornados sempre.
Como uma observação lateral, enquanto o MySQL permite que você use um
ORDER BY
em uma subconsulta e aplique aGROUP BY
a nem todas as colunas daSELECT
lista, esse comportamento não é permitido em outros bancos de dados, incluindo o SQL Server.fonte
wp_posts
nas duas colunas para obter a linha completa.GROUP BY
apenas uma coluna, não há garantia de que os valores nas outras colunas estejam sempre corretos. Infelizmente, o MySQL permite que esse tipo de SELECT / GROUP ocorra com outros produtos. Segundo, a sintaxe do uso de umaORDER BY
em uma subconsulta enquanto permitida no MySQL não é permitida em outros produtos de banco de dados, incluindo o SQL Server. Você deve usar uma solução que retorne o resultado adequado toda vez que for executado.INDEX(post_author, post_date)
é importante.post_id
sua consulta interna, tecnicamente também deve agrupá-la, o que provavelmente distorcerá seus resultados.Sua solução utiliza uma extensão da cláusula GROUP BY que permite agrupar por alguns campos (neste caso, apenas
post_author
):e selecione colunas não agregadas:
que não estão listados no grupo por cláusula ou que não são usados em uma função agregada (MIN, MAX, COUNT, etc.).
Uso correto da extensão à cláusula GROUP BY
Isso é útil quando todos os valores de colunas não agregadas são iguais para cada linha.
Por exemplo, suponha que você tenha uma tabela
GardensFlowers
(name
do jardim,flower
que cresce no jardim):e você deseja extrair todas as flores que crescem em um jardim, onde várias flores crescem. Então você tem que usar uma subconsulta, por exemplo, você pode usar isto:
Se você precisar extrair todas as flores que são as únicas no jardim, basta alterar a condição HAVING para
HAVING COUNT(DISTINCT flower)=1
, mas o MySql também permite que você use isso:sem subconsulta, não SQL padrão, mas mais simples.
Uso incorreto da extensão à cláusula GROUP BY
Mas o que acontece se você selecionar colunas não agregadas que não são iguais para cada linha? Qual é o valor que o MySql escolhe para essa coluna?
Parece que o MySql sempre escolhe o PRIMEIRO valor que encontra.
Para garantir que o primeiro valor encontrado seja exatamente o valor desejado, aplique a
GROUP BY
a uma consulta ordenada, daí a necessidade de usar uma subconsulta. Você não pode fazer isso de outra maneira.Dado que o MySql sempre escolhe a primeira linha que encontra, você está classificando corretamente as linhas antes do GROUP BY. Infelizmente, se você ler atentamente a documentação, perceberá que essa suposição não é verdadeira.
Ao selecionar colunas não agregadas que nem sempre são iguais, o MySql é livre para escolher qualquer valor, portanto o valor resultante que ele realmente mostra é indeterminado .
Vejo que esse truque para obter o primeiro valor de uma coluna não agregada é muito usado, e geralmente / quase sempre funciona, eu também o uso às vezes (por meu próprio risco). Mas como não está documentado, você não pode confiar nesse comportamento.
Este link (obrigado ypercube!) O truque GROUP BY foi otimizado para longe, mostra uma situação em que a mesma consulta retorna resultados diferentes entre o MySql e o MariaDB, provavelmente por causa de um mecanismo de otimização diferente.
Portanto, se esse truque funcionar, é apenas uma questão de sorte.
A resposta aceita na outra pergunta parece errada para mim:
wp_posts.post_date
é uma coluna não agregada e seu valor será oficialmente indeterminado, mas provavelmente será o primeiropost_date
encontrado. Porém, como o truque GROUP BY é aplicado a uma tabela não ordenada, não há certeza de qual é a primeirapost_date
encontrada.Provavelmente retornará postagens que são as únicas postagens de um único autor, mas mesmo isso nem sempre é certo.
Uma possível solução
Eu acho que isso poderia ser uma solução possível:
Na consulta interna, estou retornando a data máxima de postagem para cada autor. Então, estou levando em consideração o fato de que o mesmo autor poderia teoricamente ter duas postagens ao mesmo tempo, então estou obtendo apenas o ID máximo. E então eu estou retornando todas as linhas que têm esses IDs máximos. Isso pode ser feito mais rapidamente usando junções em vez da cláusula IN.
(Se você tem certeza de que
ID
está aumentando apenas eID1 > ID2
também significa issopost_date1 > post_date2
, a consulta pode ser muito mais simples, mas não tenho certeza se esse é o caso).fonte
extension to GROUP By
é uma leitura interessante, obrigado por isso.O que você vai ler é bastante hacky, então não tente fazer isso em casa!
No SQL em geral, a resposta para sua pergunta é NÃO , mas devido ao modo descontraído do
GROUP BY
(mencionado por @bluefeet ), a resposta é SIM no MySQL.Suponha que você tenha um índice BTREE em (post_status, post_type, post_author, post_date). Como é o índice embaixo do capô?
(post_status = 'publicar', post_type = 'post', post_author = 'usuário A', post_date = '2012-12-01') (post_status = 'publicar', post_type = 'post', post_author = 'usuário A', post_date = '2012-12-31') (post_status = 'publique', post_type = 'post', post_author = 'usuário B', post_date = '2012-10-01') (post_status = 'publique', post_type = ' post ', post_author =' usuário B ', post_date =' 2012-12-01 ')
Ou seja, os dados são classificados por todos esses campos em ordem crescente.
Quando você faz um,
GROUP BY
por padrão, ele classifica os dados pelo campo de agrupamento (post_author
no nosso caso; post_status, post_type são requeridos pelaWHERE
cláusula) e, se houver um índice correspondente, os dados de cada primeiro registro serão coletados em ordem crescente. Essa é a consulta que buscará o seguinte (a primeira postagem para cada usuário):(post_status = 'publicar', post_type = 'post', post_author = 'usuário A', post_date = '2012-12-01') (post_status = 'publicar', post_type = 'post', post_author = 'usuário B', post_date = '01/10/2012')
Mas
GROUP BY
no MySQL permite que você especifique a ordem explicitamente. E quando você solicitapost_user
em ordem decrescente, ele percorre nosso índice na ordem oposta, ainda obtendo o primeiro registro para cada grupo que é realmente o último.Isso é
nos dará
(post_status = 'publicar', post_type = 'post', post_author = 'usuário B', post_date = '2012-12-01') (post_status = 'publicar', post_type = 'post', post_author = 'usuário A', post_date = '31/12/2012')
Agora, quando você ordena os resultados do agrupamento por post_date, obtém os dados desejados.
NB :
Não é isso que eu recomendaria para essa consulta específica. Nesse caso, eu usaria uma versão ligeiramente modificada do que o @bluefeet sugere. Mas essa técnica pode ser muito útil. Dê uma olhada na minha resposta aqui: Recuperando o último registro em cada grupo
Armadilhas : As desvantagens da abordagem são que
A vantagem é o desempenho em casos difíceis. Nesse caso, o desempenho da consulta deve ser o mesmo da consulta do @ bluefeet, devido à quantidade de dados envolvidos na classificação (todos os dados são carregados em uma tabela temporária e depois classificados; btw, sua consulta também requer o
(post_status, post_type, post_author, post_date)
índice) .O que eu sugeriria :
Como eu disse, essas consultas fazem com que o MySQL perca tempo classificando quantidades potencialmente enormes de dados em uma tabela temporária. Caso você precise de paginação (ou seja, LIMIT está envolvido), a maioria dos dados é descartada. O que eu faria é minimizar a quantidade de dados classificados: isto é, ordenar e limitar um mínimo de dados na subconsulta e, em seguida, ingressar novamente na tabela inteira.
A mesma consulta usando a abordagem descrita acima:
Todas essas consultas com seus planos de execução no SQLFiddle .
fonte
Tente este. Basta obter a lista das últimas datas de postagem de cada autor . É isso aí
fonte
post_date IN (select max(...) ...)
. Isso é mais eficiente do que fazer um grupo em uma sub-seleção, consulte dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html #IN ( SELECT ... )
é muito menos eficiente que o equivalente a JOIN.Não. Não faz sentido ordenar os registros antes do agrupamento, pois o agrupamento irá alterar o conjunto de resultados. O caminho da subconsulta é o caminho preferido. Se isso estiver indo muito devagar, você terá que alterar o design da sua tabela, por exemplo, armazenando o ID da última postagem de cada autor em uma tabela separada ou introduzir uma coluna booleana indicando para cada autor qual é a última da postagem. 1.
fonte
Basta usar a função max e a função de grupo
fonte
Apenas para recapitular, a solução padrão usa uma subconsulta não correlacionada e fica assim:
Se você estiver usando uma versão antiga do MySQL ou um conjunto de dados bastante pequeno, poderá usar o seguinte método:
fonte
** Subconsultas podem ter um impacto ruim no desempenho quando usadas com grandes conjuntos de dados **
Consulta original
Consulta modificada
porque eu estou usando
max
noselect clause
==>max(p.post_date)
é possível evitar consultas sub-select e ordenar pela coluna max após o grupo por.fonte
Primeiro, não use * no select, afeta seu desempenho e dificulta o uso do grupo e a ordem de. Tente esta consulta:
Quando você não especifica a tabela em ORDER BY, apenas o alias, eles ordenam o resultado da seleção.
fonte