Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como GROUP BY corretamente no MySQL?


A primeira coisa a deixar claro é que SQL não é MySQL.

No SQL padrão não é permitido agrupar por um subconjunto dos campos não agregados. O motivo é muito simples. Suponha que eu esteja executando esta consulta:
SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color

Essa consulta não faria nenhum sentido. Mesmo tentando explicar isso seria impossível. Com certeza é selecionar cores e contar a quantidade de carros por cor. No entanto, também está adicionando o owner_name campo e pode haver muitos proprietários para uma determinada cor, como é o caso do White cor. Então, se pode haver muitos owner_name valores para uma única color que é o único campo no GROUP BY cláusula... então qual owner_name será retornado?

Se for necessário retornar um owner_name então algum tipo de critério deve ser adicionado para selecionar apenas um deles, por exemplo, o primeiro em ordem alfabética, que neste caso seria John . Esse critério resultaria na adição de uma função agregada MIN(owner_name) e então a consulta fará sentido novamente, pois será agrupada por, pelo menos, todos os campos não agregados na instrução select.

Como você pode ver, há uma razão clara e prática para o SQL padrão ser inflexível no agrupamento. Caso contrário, você pode enfrentar situações embaraçosas nas quais o valor de uma coluna será imprevisível, e essa não é uma palavra agradável, principalmente se a consulta que está sendo executada estiver mostrando as transações da sua conta bancária.

Dito isso, então por que o MySQL permitiria consultas que podem não fazer sentido? E pior ainda, o erro na consulta acima pode ser detectado sintaticamente! A resposta curta é:desempenho. A resposta longa é que existem certas situações em que, com base nas relações de dados, obter um valor imprevisível do grupo resultará em um valor previsível.

Se você ainda não descobriu, a única maneira de prever o valor que obterá ao obter um elemento imprevisível de um grupo será se todos os elementos do grupo forem iguais. Um exemplo claro dessa situação está na consulta de amostra em sua mesma pergunta. Veja como owner_id e owner_name relaciona na tabela. É claro que dado qualquer owner_id , por exemplo. 2 , você só pode ter um owner_name distinto . Mesmo tendo muitas linhas, ao escolher qualquer uma, você obterá Mike como resultado. No jargão formal de banco de dados, isso pode ser explicado como owner_id determina funcionalmente owner_name .

Vamos dar uma olhada mais de perto nessa consulta MySQL totalmente funcional:
SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id

Dado qualquer owner_id isso retornaria o mesmo owner_name , adicionando-o ao GROUP BY cláusula não resultará em mais linhas retornadas. Mesmo adicionando uma função agregada MAX(owner_name) não resultará em menos linhas retornadas. Os dados resultantes serão exatamente os mesmos. Em ambos os casos, a consulta seria imediatamente transformada em uma consulta SQL padrão legal, pois pelo menos todos os campos não agregados seriam agrupados. Portanto, existem 3 abordagens para obter os mesmos resultados.

No entanto, como mencionei antes, esse agrupamento não padrão tem uma vantagem de desempenho. Você pode verificar este link tão subestimado em que isso é explicado para mais detalhes, mas vou citar a parte mais importante:

Uma coisa que vale a pena mencionar é que os resultados não são necessariamente errados mas sim indeterminado . Em outras palavras, obter os resultados esperados não significa que você escreveu a consulta correta. Escrever a consulta certa sempre fornecerá os resultados esperados.

Como você pode ver, pode valer a pena aplicar esta extensão MySQL ao GROUP BY cláusula. De qualquer forma, se isso ainda não estiver 100% claro, existe uma regra prática que garantirá que seu agrupamento esteja sempre correto:Sempre agrupar, pelo menos, por todos os campos não agregados na cláusula select . Você pode estar desperdiçando alguns ciclos de CPU em determinadas situações, mas é melhor do que retornar indeterminado resultados. Se você ainda está com medo de não agrupar corretamente, altere o ONLY_FULL_GROUP_BY O modo SQL pode ser um último recurso :)

Que seu agrupamento seja correto e eficiente... ou pelo menos correto.