Erro relacionado a only_full_group_by ao executar uma consulta no MySql

442

Eu atualizei meu sistema e instalei o MySql 5.7.9 com php para um aplicativo da Web em que estou trabalhando. Eu tenho uma consulta que é criada dinamicamente e, quando executada em versões mais antigas do MySql, funciona bem. Desde a atualização para 5,7, recebo este erro:

A expressão nº 1 da lista SELECT não está na cláusula GROUP BY e contém a coluna não agregada 'support_desk.mod_users_groups.group_id', que não depende funcionalmente das colunas da cláusula GROUP BY; isso é incompatível com sql_mode = only_full_group_by

Observe a página Manual do Mysql 5.7 no tópico Modos SQL do servidor .

Esta é a consulta que está me dando problemas:

SELECT mod_users_groups.group_id AS 'value', 
       group_name AS 'text' 
FROM mod_users_groups
LEFT JOIN mod_users_data ON mod_users_groups.group_id = mod_users_data.group_id 
WHERE  mod_users_groups.active = 1 
  AND mod_users_groups.department_id = 1 
  AND mod_users_groups.manage_work_orders = 1 
  AND group_name != 'root' 
  AND group_name != 'superuser' 
GROUP BY group_name 
HAVING COUNT(`user_id`) > 0 
ORDER BY group_name

Eu pesquisei sobre o assunto no Google, mas não entendo o only_full_group_bysuficiente para descobrir o que preciso fazer para corrigir a consulta. Posso simplesmente desativar a only_full_group_byopção ou há outra coisa que preciso fazer?

Entre em contato se precisar de mais informações.

Dan Bemowski
fonte
Aqui eu encontrei solução stackoverflow.com/a/7588442/612987
Wasim A.
21
Essa deve ser a mensagem de erro mais complicada que eu já vi.
Rolf
2
@Rolf Você viu o erro para o mesmo tipo de problema no Oracle? " not a GROUP BY expression" É isso aí. Eles podem também ter apenas um código de erro numérico e nenhuma mensagem.
Bill Karwin

Respostas:

359

Eu apenas adicionaria group_idao GROUP BY.

Ao SELECTinserir uma coluna que não faz parte GROUP BYdela, pode haver vários valores para essa coluna dentro dos grupos, mas haverá apenas espaço para um único valor nos resultados. Portanto, o banco de dados geralmente precisa ser informado exatamente como transformar esses vários valores em um único valor. Normalmente, isso é feito com uma função de agregação, como COUNT(), SUM(), MAX()etc ... Eu digo geralmente porque a maioria dos outros sistemas de banco de dados populares insistir nisso. No entanto, no MySQL anterior à versão 5.7, o comportamento padrão foi mais indulgente porque não reclama e depois escolhe arbitrariamente qualquer valor ! Também tem umANY_VALUE()função que poderia ser usada como outra solução para essa pergunta se você realmente precisasse do mesmo comportamento de antes. Essa flexibilidade tem um custo porque é não determinística, portanto, eu não a recomendaria, a menos que você tenha um bom motivo para precisar. Agora, o MySQL está ativando a only_full_group_byconfiguração por boas razões, por isso é melhor se acostumar com isso e fazer com que suas consultas estejam em conformidade.

Então, por que minha resposta simples acima? Fiz algumas suposições:

1) o group_idé único. Parece razoável, afinal de contas, é um 'ID'.

2) o group_nametambém é único. Isso pode não ser uma suposição razoável. Se esse não for o caso, e você tiver alguma duplicata group_namese, em seguida, seguir meu conselho para adicioná group_id-la GROUP BY, poderá descobrir que agora obtém mais resultados do que antes, porque os grupos com o mesmo nome agora terão linhas separadas nos resultados. Para mim, isso seria melhor do que ocultar esses grupos duplicados porque o banco de dados selecionou discretamente um valor arbitrariamente!

Também é uma boa prática qualificar todas as colunas com o nome ou o alias da tabela quando houver mais de uma tabela envolvida ...

SELECT 
  g.group_id AS 'value', 
  g.group_name AS 'text' 
FROM mod_users_groups g
LEFT JOIN mod_users_data d ON g.group_id = d.group_id 
WHERE g.active = 1 
  AND g.department_id = 1 
  AND g.manage_work_orders = 1 
  AND g.group_name != 'root' 
  AND g.group_name != 'superuser' 
GROUP BY 
  g.group_name, 
  g.group_id 
HAVING COUNT(d.user_id) > 0 
ORDER BY g.group_name
davmos
fonte
Obrigado, isso funciona perfeito. Também concordo que preciso qualificar as colunas. Elimina qualquer possibilidade de ambiguidade. Estou no processo de reparar o código agora. Um milhão de agradecimentos pela ajuda.
Dan Bemowski
Eu nem tenho uma GROUP BYcláusula na minha consulta, mas estou recebendo esse erro.
aitchkhan
@HaroonKhan, isso soa como uma nova pergunta. Deixe-me saber se você perguntar e eu vou dar uma olhada.
Davmos 29/08
2
No MySQL 5.7, eles definem uma propriedade que requer que todos os campos não agregados em uma consulta sejam GROUP BY. Portanto, uma consulta como SELECT a, SUM (b) FROM table; significa que o campo "a" deve estar em um GROUP BY. Portanto, se você não tiver um GROUP BY, deverá adicioná-lo à consulta. É tudo sobre se você tiver pelo menos um campo agregado na parte SELECT da sua consulta.
User1567291
Eu tive que corrigir o "aviso" adicionando o item ao grupo por cláusula, como sugerido nesta resposta, porque nenhuma das respostas que sugerem correções sql_mode funcionou. (meu MySQL estava em um procedimento armazenado, mas não sei se isso era relevante)
ZZAPPER
354

Você pode tentar desativar a only_full_group_byconfiguração executando o seguinte:

mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

O MySQL 8 não aceita e, NO_AUTO_CREATE_USERportanto, precisa ser removido.

WeiYuan
fonte
85
Este é um trabalho ao redor, você fixar melhor a sua consulta, em vez de silenciar o aviso
azerafati
1
Atualizar o /etc/mysql/my.cnf (como visto abaixo) é uma solução mais sustentável, pois as configurações padrão tendem a voltar após a reinicialização. E para aqueles que dizem que você deve corrigir a consulta, isso não é muito fácil, às vezes, quando você precisa de uma consulta de depuração rápida e faz coisas como SELECT COUNT(*), t.* FROM my_table t GROUP BY colvocê tem 20 a 50 colunas, deseja gastar tanto tempo adicionando cada coluna ao agrupar por?
Shadoweb 8/06
3
Essa "solução" é bastante perigosa. Você pode ativar os modos desativados anteriormente, alterando-os às cegas, em vez de remover apenas o sinalizador de configurações em questão. Isso pode levar a um comportamento inesperado e, na pior das hipóteses, levar a resultados errados ou impedir que seus aplicativos funcionem completamente. Portanto, considero esta resposta errada.
Chris S.
1
Eu fiz este persistente através de atualização por descobrir minha sql_mode atual via SELECT @@sql_mode;e, em seguida, adicionando o resultado de lá para my.cnf:sql_mode=[list of modes from query, minus ONLY_FULL_GROUP_BY]
wbharding
323

você pode desativar a mensagem de aviso conforme explicado nas outras respostas ou pode entender o que está acontecendo e corrigi-lo.

No MySQL 5.7.5, o modo SQL padrão inclui ONLY_FULL_GROUP_BY, o que significa que quando você agrupa linhas e seleciona algo dentre esses grupos, é necessário dizer explicitamente de qual linha a seleção deve ser feita. O Mysql precisa saber qual linha do grupo você está procurando, o que oferece duas opções

O Mysql precisa saber qual linha do grupo você está procurando, o que oferece duas opções

  • Você também pode adicionar a coluna que você deseja à instrução de grupo, group by rect.color, rect.valueque pode ser o que você quer em alguns casos, caso contrário, retornaria resultados duplicados com a mesma cor que você pode não querer
  • você também pode usar funções agregadas do mysql para indicar qual linha você está procurando dentro dos grupos, como AVG() MIN() MAX() lista completa
  • E, finalmente, você pode usar ANY_VALUE()se tiver certeza de que todos os resultados dentro do grupo são iguais. doc
azerafati
fonte
25
Eu não acho que o MySQL suporta o FIRST()método / função?
Ben Aliança
3
Eu acredito que o MySQL (pelo menos antes da 5.7) assume como padrão o primeiro valor encontrado em um grupo. Portanto, não existe o FIRST () porque costumava ser sinônimo da função GROUP BY.
DrDamnit
2
@DrDamnit, eu creio que foi mais como seleção aleatória, nesses casos, que levaram à confusão e permitindo, assim, ONLY_FULL_GROUP_BYpor padrão
azerafati
6
Eu já sabia a resposta, mas o exemplo com o retângulo colorido é tão fácil de entender que vou reutilizá-lo na próxima vez que alguém me perguntar.
user327961
@azerafati existe algo como FIRST () ou alguma consulta que pode ser usada para obter o primeiro valor, como é o caso comigo.
Haris
169

Se você não quiser fazer alterações na sua consulta atual, siga as etapas abaixo -

  1. ssh vagrant em sua caixa
  2. Tipo: sudo vim /etc/mysql/my.cnf
  3. Role até a parte inferior do arquivo e digite Apara entrar no modo de inserção
  4. Copiar e colar

    [mysqld]
    sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
  5. Digite escpara sair do modo de entrada

  6. Digite :wqpara salvar e fechar o vim.
  7. Digite sudo service mysql restartpara reiniciar o MySQL.
Ajai
fonte
Apenas um aviso a todos os usuários do Vagrant que tentam usar isso com o AdonisJS, é necessário para executar a paginatefunção.
22716 Michael J. Calkins
Obviamente, isso não é apenas para usuários do Vagrant, mas para qualquer sistema Unix. Observe que para mim a localização do my.cnfarquivo era /etc. Als observe a nova sintaxe desde:MySQL 5.7.8: sql-mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
Valentin Grégoire
Funciona para mim no Mint 19.3 (ubuntu 18.04) obrigado
Marco López
71

Use ANY_VALUE()para se referir à coluna não agregada.

SELECT name,           address , MAX(age) FROM t GROUP BY name; -- fails
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name; -- works

Dos documentos do MySQL 5.7 :

Você pode obter o mesmo efeito sem desativar ONLY_FULL_GROUP_BY usando ANY_VALUE()para se referir à coluna não agregada.

...

Esta consulta pode ser inválida com ONLY_FULL_GROUP_BYativado porque a coluna de endereço não agregado na lista de seleção não é nomeada na GROUP BYcláusula:

SELECT name, address, MAX(age) FROM t GROUP BY name;

...

Se você souber que, para um determinado conjunto de dados, cada valor de nome de fato determina exclusivamente o valor do endereço, o endereço é efetivamente funcionalmente dependente do nome. Para dizer ao MySQL para aceitar a consulta, você pode usar a ANY_VALUE()função:

SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
Italo Borssatto
fonte
1
Como alguém que já se deparava com esse problema há algum tempo - eu gosto especialmente desta solução porque ela não exige que você altere nenhum arquivo ou configuração na sua configuração do MySQL. Curiosamente, eu não vejo ANY_VALUE mencionado em nenhum outro lugar em discussões semelhantes - funciona perfeitamente para mim com o meu GROUP_BY contains nonaggregated columnerro.
adstwlearn
50

Vou tentar explicar o que é esse erro.
A partir do MySQL 5.7.5, a opção ONLY_FULL_GROUP_BYé ativada por padrão.
Assim, de acordo com o padrão SQL92 e anterior:

não permite consultas para as quais a lista de seleção, a condição HAVING ou a lista ORDER BY se referem a colunas não agregadas que não são nomeadas na cláusula GROUP BY nem dependem funcionalmente das colunas GROUP BY (determinadas exclusivamente por)

( leia mais em documentos )

Então, por exemplo:

SELECT * FROM `users` GROUP BY `name`;

Você receberá uma mensagem de erro após executar a consulta acima.

# 1055 - A expressão nº 1 da lista SELECT não está na cláusula GROUP BY e contém a coluna não agregada 'testsite.user.id', que não depende funcionalmente das colunas da cláusula GROUP BY; isso é incompatível com sql_mode = only_full_group_by

Por quê?
Como o MySQL não entende exatamente, quais valores de registros agrupados devem ser recuperados, e esse é o ponto.

IE, digamos que você tenha esses registros em sua userstabela:
Yo

E você executará uma consulta inválida mostrada acima.
E você receberá o erro mostrado acima, porque há 3 registros com o nome Johne é bom, mas todos eles têm emailvalores de campo diferentes .
Portanto, o MySQL simplesmente não entende qual deles retornar no registro agrupado resultante.

Você pode corrigir esse problema, alterando sua consulta da seguinte maneira:

SELECT `name` FROM `users` GROUP BY `name`

Além disso, convém adicionar mais campos à seção SELECT, mas não é possível fazer isso, se não estiverem agregados, mas houver muleta que você possa usar (mas não é altamente recomendado):

SELECT ANY_VALUE(`id`), ANY_VALUE(`email`), `name` FROM `users` GROUP BY `name`

insira a descrição da imagem aqui

Agora, você pode perguntar: por que o uso ANY_VALUEnão é altamente recomendado?
Como o MySQL não sabe exatamente qual o valor dos registros agrupados a serem recuperados, e usando esta função, você solicita que ele busque qualquer um deles (neste caso, foi obtido o email do primeiro registro com o nome = John).
Exatamente, não posso ter nenhuma idéia de por que você gostaria que esse comportamento existisse.

Por favor, se você não me entende, leia mais sobre como o agrupamento no MySQL funciona, é muito simples.

E no final, aqui está mais uma consulta simples e válida.
Se você deseja consultar a contagem total de usuários de acordo com as idades disponíveis, anote esta consulta

SELECT `age`, COUNT(`age`) FROM `users` GROUP BY `age`;

O que é totalmente válido, de acordo com as regras do MySQL.
E assim por diante.

É importante entender qual é exatamente o problema e só então anotar a solução.

Abraham Tugalov
fonte
Obrigado por esta boa resposta. E se eu quiser os emails também, como uma matriz no grupo de resultados?
1937 Josiah
1
Você sempre pode GROUP_CONCAT campo obrigatório para obter todas as entradas existentes.
Abraham Tugalov
Oh! então tem um jeito! Por favor, ajude-me nesta pergunta
Josiah
Obrigado por esta ótima resposta. E se eu quiser fazer GROUP BY DAY(created_date)? Parece que não funciona se DAY()for adicionado.
esquimó
41

Estou usando o Laravel 5.3, mysql 5.7.12, no laravel homestead (0.5.0, acredito)

Mesmo depois de definir explicitamente a edição /etc/mysql/my.cnfpara refletir:

[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Eu ainda estava recebendo o erro.

Eu tive que mudar config/database.phpde truepara false:

    'mysql' => [
        'strict' => false, //behave like 5.6
        //'strict' => true //behave like 5.7
    ], 

Leitura adicional:

https://laracasts.com/discuss/channels/servers/set-set-sql-mode-on-homestead https://mattstauffer.co/blog/strict-mode-and-other-mysql-customizations-in-laravel -5-2

Ryan
fonte
Dang! Isso foi útil para os usuários do Laravel. +1 para isso.
Okafor T Kosiso
20

Se você estiver usando o wamp 3.0.6 ou qualquer versão superior que não seja a estável 2.5, poderá enfrentar esse problema, primeiramente o problema é com o sql. você deve nomear os campos adequadamente. mas há outra maneira pela qual você pode resolvê-lo. clique no ícone verde do wamp. mysql-> mysql settings-> sql_mode-> none. ou no console, você pode alterar os valores padrão.

mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
PiyaChakraborty
fonte
fantástico, construindo sobre o que ele disse, inserir comandos show variables like '%sql_mode%';em linha de comando mysql, configuração sql_mode irá expor
Jade Han
19

Adição de linhas (mencione abaixo) no arquivo: /etc/mysql/my.cnf

[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Funciona bem para mim. Versão do servidor: 5.7.18-0ubuntu0.16.04.1 - (Ubuntu)

Jagdeep Singh
fonte
1
você está usando o RDS?
Mubasshir Pawle
@MubasshirPawle No
Jagdeep Singh
16

Vá para mysql ou phpmyadmin e selecione database, então simplesmente execute esta consulta e ela funcionará. Está funcionando bem para mim.

SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
Anil Gupta
fonte
Great..Thank :)
mohitesachin217
Meu prazer :) @mohit
Anil Gupta
Economizei muito tempo, obrigado.
precisa saber é o seguinte
1
Bem-vindo mano @TharinduEranga
Anil Gupta
1
Mas o problema com a solução é que toda vez que reinicio o mysql, preciso executar a consulta novamente. Isso significa que a solução não é permanente ou persistente.
Mushfiqur Rahman 25/01
10

Para mac:

1.Copie o padrão my-default.cnf para /etc/my.cnf

sudo cp $(brew --prefix mysql)/support-files/my-default.cnf /etc/my.cnf

2. Altere sql_mode no my.cnf usando seu editor favorito e configure-o para este

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Reinicie o servidor MySQL.

mysql.server restart
Tanvir Hasan Anick
fonte
Obrigado, trabalhou para mim também. Mas eu uso o mysql padrão do Mac, então não tenho o caminho de preparação. Apenas sudo cp my-default.cnf /etc/my.cnf
Mike Nguyen
2
@Mike Nguyen sudo cp /usr/local/mysql-5.7.17-macos10.12-x86_64/support-files/my-default.cnf /etc/my.cnf sudo vi /etc/my.cnf define sql_model sql_mode = STRICT_TRANS_TABLES , NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION Reinicie o servidor MySQL.
Yong Gao
7

Foi isso que me ajudou a entender toda a questão:

  1. https://stackoverflow.com/a/20074634/1066234
  2. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

E no seguinte outro exemplo de uma consulta problemática.

Problemático:

SELECT COUNT(*) as attempts, SUM(elapsed) as elapsedtotal, userid, timestamp, questionid, answerid, SUM(correct) as correct, elapsed, ipaddress FROM `gameplay`
                        WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 1 DAY)
                        AND cookieid = #

Resolvido adicionando isso ao final:

  GROUP BY timestamp, userid, cookieid, questionid, answerid, elapsed, ipaddress

Nota: Veja a mensagem de erro no PHP, que indica onde está o problema.

Exemplo:

Erro de consulta MySQL 1140: Na consulta agregada sem GROUP BY, a expressão nº 4 da lista SELECT contém a coluna não agregada 'db.gameplay.timestamp'; isso é incompatível com sql_mode = only_full_group_by - Consulta: SELECT COUNT (*) como tentativas, SUM (decorrido) como elapsedtotal, userid, timestamp, questionid, answerid, SUM (correto) como correto, decorrido, endereço de IP FROM gameplay WHERE timestamp> = DATE_SUB (NOW (), INTERVALAR 1 DIA) AND userid = 1

Nesse caso, a expressão # 4 estava ausente no GROUP BY.

Kai Noack
fonte
1
Obrigado por apontar stackoverflow.com/questions/20074562/…
cwhsu
Ótima resposta para resolver esse problema. Muito obrigado
Hansana Athukorala
7

Para localhost / wampserver 3, podemos definir o modo sql = user_mode para remover este erro:

click on wamp icon -> MySql -> MySql Setting -> sql-mode -> user_mode

depois reinicie o wamp ou apache

Braham Dev Yadav
fonte
1
Sei que isso pode não ser uma correção de longo prazo para esse problema, mas certamente ajudou por enquanto. Minha empresa de hospedagem, Go Daddy, está usando o MySQL V5.6.33 e o WampServer 3 usa o MySQL V5.7.14. Obrigado por esta solução rápida
Alan N
4

Você pode adicionar um unique indexa group_id; se você tiver certeza de que group_idé único.

Pode resolver seu caso sem modificar a consulta .

Uma resposta tardia, mas ainda não foi mencionada nas respostas. Talvez deva completar as respostas já abrangentes disponíveis. Pelo menos, resolveu meu caso quando tive que dividir uma tabela com muitos campos.

micaball
fonte
2

Se você tiver esse erro no Symfony usando o construtor de consultas de doutrina e se esse erro for causado por um orderBy :

Preste atenção selectna coluna que você deseja groupBye use em addGroupByvez de groupBy:

$query = $this->createQueryBuilder('smth')->addGroupBy('smth.mycolumn');

Trabalhos no Symfony3 -

Gangai Johann
fonte
1

Desculpas por não usar seu SQL exato

Eu usei essa consulta para superar o aviso do Mysql.

SELECT count(*) AS cnt, `regions_id`
FROM regionables 
WHERE `regionable_id` = '115' OR `regionable_id` = '714'
GROUP BY `regions_id`
HAVING cnt > 1

note a chave para mim ser

count(*) AS cnt
Harry Bosh
fonte