20th Jul 2022 10 minutos de leitura Como funciona o SQL GROUP BY? Ignacio L. Bisso sql aprender sql group by Índice GROUP BY, Parte 1: Agrupamento de dados GROUP BY, Parte 2: Funções de Agregação Agrupamento de Registros por Várias Colunas Agrupando valores NULL Usando WHERE com GROUP BY Evitando problemas com o GROUP BY Problemas de contagem Omitindo Colunas Não Agregadas de GROUP BY Há mais o que fazer com GROUP BY O agrupamento de resultados é um poderoso recurso SQL que permite calcular as principais estatísticas para um grupo de registros. GROUP BY é uma das cláusulas mais poderosas do SQL. Ele permite que você veja os dados de uma nova maneira e encontre métricas chave (como a média, valores máximos e mínimos em um grupo de registros). Sem GROUP BY, todos os resultados que obtemos são orientados para os registros. Com GROUP BY, podemos criar grupos de registros e calcular métricas sobre cada grupo. Neste artigo, você aprenderá como o GROUP BY torna suas consultas SQL muito mais poderosas e diversificadas. GROUP BY, Parte 1: Agrupamento de dados Vamos supor que temos um pequeno hotel na Patagônia. Também temos um banco de dados contendo nomes dos hóspedes, cidades de origem, idades, datas de check-in, datas de check-out, e muito mais. Estes dados estão em duas tabelas chamadas room_guest e guest. Dê uma olhada: room_guest guest_nameorigin_cityroom_numberday_inday_outageroom_levelamount_invoiced Juan B.San Pedro10012012-12-282013-01-0732standard$9500 Mary J.San Francisco10022013-01-022013-01-1223standard$6700 Peter S.Dubai20022013-01-022013-01-2965premium$34000 Clair BGenova20012014-07-022014-08-0221standard$16000 Meiling Y.San Francisco20022014-11-022014-11-1252standard$9500 Olek V.Dubai20032015-01-022015-01-3137premium$28400 Benjamin L.San Pedro20022016-01-022016-01-1561premium$15400 Arnaldo V.Genova10012017-01-012017-01-0443standard$2500 Mary J.San Francisco10022017-01-022017-01-0723standard$4800 Wei W.Los Angeles20022018-01-022018-01-2231standard$12000 Meiling Y.San Francisco20012018-01-022018-01-2252premium$17500 Peter S.Dubai20022019-01-022019-02-2565premium$32000 Arnaldo V.Genova20032019-08-052019-08-1743standard$11200 Mary J.San Francisco10012019-01-022019-01-1223standard$8900 guest guest_namepreferred_activitycity_namestatecountrycontinent activityCity_nameStateCountryContinent32 Juan B.trekkingSan PedroAndaluciaSpainEurope Mary J.trekkingSan FranciscoCaliforniaUnited StatesAmerica Peter S.trekkingDubaiDubaiArabiaAsia Chiara BskiingGenovaLiguriaItalyEurope Meiling Y.trekkingSan FranciscoCaliforniaUnited StatesAmerica Olek V.relaxingDubaiDubaiArabiaAsia Benjamin L.skiingSan PedroBuenos AiresArgentinaAmerica Wei W.trekkingLos AngelesCaliforniaUnited StatesAmerica Arnaldo V.skiingGenovaLiguriaItalyEurope Queremos calcular algumas estatísticas para que possamos reservar mais hóspedes. A cláusula SQL GROUP BY nos permite agrupar registros com base em dados em uma determinada coluna (ou colunas). Podemos agrupar os registros na tabela room_guest com base no valor da coluna origin_city. Então todos os registros de convidados de 'Gênova' pertencerão a um grupo; todos os registros de convidados de 'Dubai' pertencerão a outro grupo, e assim por diante. A tabela a seguir mostra cada grupo de registros em uma cor diferente. guest_nameorigin_cityroom_numberday_inday_outageroom_levelamount_invoiced Peter S.Dubai20022013-01-022013-01-2965premium$34000 Olek V.Dubai20032015-01-022015-01-3137premium$28400 Peter S.Dubai20022019-01-022019-02-2565premium$32000 Clair BGenova20012014-07-022014-08-0221standard$16000 Arnaldo V.Genova10012017-01-012017-01-0443standard$2500 Arnaldo V.Genova20032019-08-052019-08-1743standard$11200 Wei W.Los Angeles20022018-01-022018-01-2231standard$12000 Mary J.San Francisco10022013-01-022013-01-1223standard$6700 Mary J.San Francisco10022017-01-022017-01-0723standard$4800 Meiling Y.San Francisco20022014-11-022014-11-1252standard$9500 Meiling Y.San Francisco20012018-01-022018-01-2256premium$17500 Mary J.San Francisco10012019-01-022019-01-1223standard$8900 Benjamin L.San Pedro20022016-01-022016-01-1561premium$15400 Juan B.San Pedro10012012-12-282013-01-0732standard$9500 Agora, suponha que o proprietário do hotel queira saber quantos hóspedes vêm de cada cidade. Para descobrir, precisamos contar o número de registros em cada grupo. Em outras palavras, precisamos da função agregada COUNT(*), que retorna o número de registros em um grupo. COUNT() é uma função muito comum; voltaremos a ela mais tarde neste artigo. Portanto, precisamos de uma consulta para criar grupos de registros com o mesmo valor em origin_city e depois contar o número de registros em cada grupo. A consulta seria parecida com esta: SELECT origin_city, COUNT(*) AS quantity_of_guests FROM room_guest GROUP BY origin_city Você pode comparar o número de convidados de cada cidade na tabela de resultados abaixo com a tabela colorida mostrada anteriormente: origin_cityquantity_of_guests Dubai3 Genova3 Los Angeles1 San Francisco5 San Pedro2 Observe que o número de linhas nos resultados da consulta é o mesmo que a quantidade de grupos criados pela cláusula GROUP BY. Um grupo para cada cidade, uma linha para cada cidade. Para encerrar esta introdução a GROUP BY, gostaria de sugerir a leitura do artigo Getting the Hang of the GROUP BY Clause. Ele inclui uma descrição completa do GROUP BY e vários exemplos de seus erros mais comuns. GROUP BY, Parte 2: Funções de Agregação Embora o agrupamento por um valor seja útil, o verdadeiro poder de GROUP BY é quando é usado com funções agregadas. Eu diria até mesmo que cada consulta SQL usando uma cláusula GROUP BY deve ter pelo menos uma função agregada. (Mas não é obrigatório). Na seção anterior, mencionamos que o GROUP BY é usado para criar grupos e calcular métricas. As métricas são calculadas por funções de agregação como COUNT(), SUM(), AVG(), MIN(), e MAX(). Os valores calculados por cada uma destas funções são auto-explicativos. Entretanto, todas elas têm algo em comum: todas as funções agregadas retornam um valor baseado em todos os registros do grupo. Vamos considerar um exemplo. O proprietário do hotel quer saber o valor máximo faturado para cada quarto. Além disso, ele quer ver o valor mínimo e o valor médio faturado para cada quarto. Aqui está a consulta, seguida dos resultados: SELECT room_number, MAX(amount_invoiced) AS max_amount_invoiced, MIN(amount_invoiced) AS min_amount_invoiced, AVG(amount_invoiced) AS average_amount_invoiced FROM room_guest GROUP BY room_number room_numbermax_amount_invoicedmin_amount_invoicedaverage_amount_invoiced 10019500.002500.006966.66 10026700.004800.005750.00 200117500.0016000.0016750.00 200234000.009500.0020580.00 200328400.0011200.0019800.00 Agrupamento de Registros por Várias Colunas Em alguns casos, podemos precisar agrupar por duas ou mais colunas. Podemos fazer isso com GROUP BY? Com certeza podemos! Na consulta anterior, criamos um relatório analisando quanto dinheiro cada sala está gerando. Entretanto, algumas salas podem ser configuradas em um nível premium ou standard (ver sala número 2002) durante diferentes épocas; assim, para fazer uma análise correta, precisamos agrupar os registros usando duas colunas: room_number e room_level. Antes de ir para a consulta, vamos usar cores para ver como os registros são agrupados pela cláusula GROUP BY room_number, room_level. Lembre-se de que os registros em cada grupo devem ter exatamente os mesmos valores tanto em room_number quanto em room_level. Por exemplo, o primeiro grupo é para room_number = 1001 e room_level = ‘standard’. guest_nameorigin_cityroom_numberday_inday_outageroom_levelamount_invoiced Mary J.San Francisco10012019-01-022019-01-1223standard$8900 Arnaldo V.Genova10012017-01-012017-01-0443standard$2500 Juan B.San Pedro10012012-12-282013-01-0732standard$9500 Mary J.San Francisco10022013-01-022013-01-1223standard$6700 Mary J.San Francisco10022017-01-022017-01-0723standard$4800 Meiling Y.San Francisco20012018-01-022018-01-2252premium$17500 Clair BGenova20012014-07-022014-08-0221standard$16000 Benjamin L.San Pedro20022016-01-022016-01-1561premium$15400 Peter S.Dubai20022013-01-022013-01-2965premium$34000 Peter S.Dubai20022019-01-022019-02-2565premium$32000 Meiling Y.San Francisco20022014-11-022014-11-1252standard$9500 Wei W.Los Angeles20022018-01-022018-01-2231standard$12000 Olek V.Dubai20032015-01-022015-01-3137premium$28400 Arnaldo V.Genova20032019-08-052019-08-1743standard$11200 A consulta é: SELECT room_number, room_level, MAX(amount_invoiced) AS max_amount_invoiced, MIN(amount_invoiced) AS min_amount_invoiced, AVG(amount_invoiced) AS average_amount_invoiced FROM room_guest GROUP BY room_number, room_level A tabela a seguir mostra os resultados desta consulta. Você pode comparar esta tabela com a tabela anterior para verificar os resultados. room_numberroom_levelmax_amount_invoicedmin_amount invoicedaverage_amoun_invoiced 1001standard9500.002500.006966.66 1002standard6700.004800.005750.00 2001premium17500.0017500.0017500.00 2001standard16000.0016000.0016000.00 2002premium34000.0015400.0027133.33 2002standard12000.009500.0010750.00 2003premium28400.0028400.0028400.00 2003standard11200.0011200.0011200.00 Agrupando valores NULL Como qualquer outro valor, NULL valores têm seu próprio grupo; se tivermos um NULL em qualquer uma das colunas em GROUP BY, um grupo extra de registros é criado para esses registros. Para demonstrar isto, precisamos inserir um par de registros com valores NULL na coluna origin_city: INSERT INTO into room_guest VALUES ('Kevin C.', NULL, 2001, '2019-07-25', '2019-08-07', NULL, 'standard', 10500); INSERT INTO into room_guest VALUES ('Karl J.', NULL, 1002, '2019-11-12', '2019-11-22', NULL, 'premium', 13900); Em seguida, esta consulta ... SELECT origin_city, COUNT(*) AS quantity_of_guests FROM room_guest GROUP BY origin_city ... mostrará o seguinte resultado. Observe o novo grupo para os valores NULL origin_city na primeira linha: origin_cityquantity_of_guests NULL2 Dubai3 Genova3 Los Angeles1 San Francisco5 San Pedro2 Usando WHERE com GROUP BY A cláusula WHERE é freqüentemente utilizada nas consultas SQL, portanto é importante entender como funciona quando combinada com GROUP BY. A cláusula WHERE é aplicada antes do GROUP BY. Isto significa que todos os registros são filtrados primeiro por WHERE; depois os registros que correspondem à condição WHERE são agrupados usando o critério GROUP BY. Como exemplo, vamos usar a consulta anterior, mas desta vez vamos filtrar para convidados vindos das cidades de São Francisco e Los Angeles. A consulta é: SELECT room_number, room_level, MAX(amount_invoiced) AS max_amount_invoiced, MIN(amount_invoiced) AS min_amount_invoiced, AVG(amount_invoiced) AS average_amount_invoiced FROM room_guest WHERE origin_city IN (‘San Francisco’,’Los Angeles’ ) GROUP BY room_number, room_level Como esperado, este conjunto de resultados é mais curto que os anteriores; a cláusula WHERE filtrou muitos hóspedes, e apenas os registros para quartos em São Francisco e Los Angeles foram processados pela cláusula GROUP BY. room_numberroom_levelmax_amount_invoicedmin_amount_ invoicedaverage_amount_invoiced 1001standard8900.008900.008900.00 1002standard6700.004800.005750.00 2001premium17500.0017500.0017500.00 2002standard12000.009500.0010750.00 Evitando problemas com o GROUP BY Quando você está começando com GROUP BY, é comum se deparar com os seguintes problemas Veja aqui como evitá-los. Problemas de contagem Vejamos um caso semelhante em que precisamos acrescentar mais de uma coluna extra na cláusula GROUP BY. Na primeira consulta, agrupamos por origin_city. No entanto, algumas cidades compartilham o mesmo nome (porque estão em estados ou países diferentes). Em nosso conjunto de dados, temos duas cidades diferentes chamadas San Pedro, uma na Argentina e a outra na Espanha. Não queremos contá-las juntas, pois elas são dois lugares diferentes. Para contar essas cidades separadamente, precisamos agrupar os registros usando as colunas city_origin, state, e country. Em seguida, repetiremos a primeira consulta, mas adicionaremos as colunas state e country à cláusula GROUP BY. Entretanto, se adicionarmos colunas ao GROUP BY, devemos adicioná-las também ao SELECT. Porque as colunas indicam e o país estão na coluna guest tabela, temos que JOIN as tabelas room_guest e guest. Aqui está a pergunta que temos: SELECT origin_city, state, country COUNT(*) AS number_of_guests FROM room_guest JOIN guest ON guest.guest_name = room_guest.guest_name GROUP BY origin_city, state, country Os resultados mostram duas cidades "San Pedro" diferentes porque utilizamos state e country como colunas adicionais na cláusula GROUP BY. origin_citystatecountrynumber_of_guests DubaiDubaiUAE3 GenovaLiguriaItaly3 Los AngelesCaliforniaUnited States1 San FranciscoCaliforniaUnited States5 San PedroBuenos AiresArgentina1 San PedroAndaluciaSpain1 Ainda há um problema a ser resolvido nesta consulta: se a mesma pessoa visitou o hotel duas vezes, estamos contando esta pessoa duas vezes. Isto não é necessariamente errado, mas e se quisermos saber o número de visitantes únicos ao hotel? Precisaríamos usar COUNT(distinct guest_name). A função de agrupamento COUNT(distinct column) retorna a quantidade de valores únicos para uma determinada coluna em um grupo de registros. Na consulta abaixo, adicionamos a função COUNT(distinct). Também mantemos o COUNT(*) original para que o leitor possa comparar os dois resultados: SELECT origin_city, state, country COUNT(distinct guest_name) AS number_of_unique_guests, COUNT(*) AS number_of_guests FROM room_guest JOIN guest ON guest.guest_name = room_guest.guest_name GROUP BY origin_city, state, country Agora podemos ver que o hotel recebeu um total de três visitas de um residente de Dubai, mas que estas três visitas foram feitas por duas pessoas distintas (Peter S. e Olek V) . origin_citystatecountrynumber_of_unique_guestsnumber_of_guests DubaiDubaiUAE23 GenovaLiguriaItaly23 Los AngelesCaliforniaUnited States11 San FranciscoCaliforniaUnited States25 San PedroBuenos AiresArgentina11 San PedroAndaluciaSpain11 Antes de fechar esta seção, sugiro que assista a este vídeo de 5 minutos em GROUP BY para iniciantes. É uma maneira super dinâmica de aprender SQL. Omitindo Colunas Não Agregadas de GROUP BY Outro erro muito comum GROUP BY é adicionar uma coluna não agregada (ou seja, uma coluna que não é usada em uma função agregada) no SELECT que você não tem em GROUP BY. Para evitar este erro, siga uma regra muito simples: Todas as colunas em SELECT devem aparecer na cláusula GROUP BY ou ser usadas em uma função agregada. Vamos tentar uma consulta inválida para ver o erro: SELECT room_number, room_level, origin_city, --This column is invalid, is not in the GROUP BY COUNT(*) AS quantity_of_visitors, FROM room_guest GROUP BY room_number, room_level Se executarmos esta consulta, obteremos o seguinte erro: ERROR: The column «room_guest.origin_city» must be in the GROUP BY clause LINE 3: guest_age, Podemos corrigir o erro adicionando a coluna origin_city à cláusula GROUP BY: SELECT room_number, room_level, Origin_city, COUNT(*) AS quantity_of_visitors FROM room_gest GROUP BY room_number, room_level, origin_city -- origin_city added Se você estiver tentando descobrir a diferença entre GROUP BY e ORDER BY, leia a Diferença entre GROUP BY e ORDER BY em Simple Words. Isso o ajudará a resolver o problema. Há mais o que fazer com GROUP BY Assim, aprendemos a utilizar GROUP BY para agrupar os registros por valores comuns. Conhecemos as funções agregadas MIN(), MAX(), AVG(), e SUM() computam várias estatísticas. E a função COUNT() faz um monte de coisas: COUNT(*) conta todas as filas. COUNT(guest_name) conta todos os valores não-NULL na coluna guest_name. COUNT(distinct guest_name) conta todos os diferentes valores não-NULL na coluna guest_name. Ao agrupar, NULL obtém seu próprio grupo. E todas as colunas não agregadas em SELECT devem estar presentes em GROUP BY. Devido à extensão do artigo, eu não cobri a cláusula HAVING, que é uma espécie de cláusula WHERE usada para filtrar grupos em vez de registros. Para aqueles leitores que querem ir um passo adiante, deixarei um link para o nosso SQL para Iniciantes curso, que cobre muitos tópicos interessantes. É uma ótima maneira de construir suas habilidades SQL! Tags: sql aprender sql group by