MODE() WITHIN GROUP (ORDER BY col) retorna o valor mais frequente de uma coluna — a moda estatistica — em uma unica expressao limpa, sem subconsultas nem funcoes de janela. E um agregado de conjunto ordenado, e o ORDER BY decide qual valor vence quando as frequencias empatam.
Sintaxe basica
A moda e o valor que aparece com mais frequencia. No PostgreSQL ela e escrita como um agregado de conjunto ordenado: tanto WITHIN GROUP quanto ORDER BY sao obrigatorios.
SELECT MODE() WITHIN GROUP (ORDER BY status) AS top_status
FROM orders;
SELECT MODE() WITHIN GROUP (ORDER BY country) AS top_country
FROM users;
Vale lembrar:
- O
ORDER BY aqui nao ordena o resultado; ele nomeia a coluna sobre a qual a moda e calculada e fornece a regra de desempate.
NULL e ignorado na contagem de frequencias, como em qualquer agregado comum.
- Funciona com qualquer tipo ordenavel: texto, numeros, datas, enums.
Moda por grupo
Na maioria das vezes voce quer a moda por recorte, nao global: o status mais frequente de cada usuario, o departamento mais popular por gerente. Adicione GROUP BY.
SELECT user_id,
MODE() WITHIN GROUP (ORDER BY status) AS usual_status
FROM orders
GROUP BY user_id;
SELECT manager_id,
MODE() WITHIN GROUP (ORDER BY dept) AS main_dept
FROM employees
GROUP BY manager_id;
A expressao se le como uma unica coluna no SELECT, entao outros agregados — COUNT(*), SUM(amount) e por ai vai — convivem ao lado dela sem joins extras.
Resolucao de empates
O detalhe central: se varios valores compartilham a frequencia maxima, MODE() retorna aquele que vem primeiro segundo o ORDER BY informado. Isso torna o resultado deterministico — ao contrario de um LIMIT 1 sem ordenacao explicita.
SELECT MODE() WITHIN GROUP (ORDER BY status) AS top_status
FROM orders;
SELECT MODE() WITHIN GROUP (ORDER BY amount DESC) AS top_amount
FROM orders;
Pegadinha: DESC no ORDER BY nao significa "o valor menos frequente". A moda continua sendo escolhida pela frequencia maxima; DESC so muda qual dos valores igualmente frequentes vence o empate. Achar um valor raro exige uma consulta totalmente diferente com COUNT(*) ordenado de forma ascendente.
Versus GROUP BY + COUNT + LIMIT 1
A forma classica de achar uma moda e contar frequencias e pegar a linha do topo:
SELECT status, COUNT(*) AS cnt
FROM orders
GROUP BY status
ORDER BY cnt DESC
LIMIT 1;
Funciona, mas tem pontos fracos frente ao MODE():
- Em um empate,
LIMIT 1 pega uma linha arbitraria a menos que voce adicione uma segunda chave de ordenacao — o resultado nao e deterministico.
- Para uma moda por grupo e preciso recorrer a uma funcao de janela (
ROW_NUMBER() OVER (PARTITION BY ...)) ou a uma subconsulta correlacionada — bem mais codigo.
- Nao da para colocar outros agregados do mesmo agrupamento ao lado em um
SELECT simples.
O mesmo resultado "moda por usuario" com uma janela fica assim:
SELECT user_id, status AS usual_status
FROM (
SELECT user_id, status, COUNT(*) AS cnt,
ROW_NUMBER() OVER (PARTITION BY user_id
ORDER BY COUNT(*) DESC, status) AS rn
FROM orders
GROUP BY user_id, status
) t
WHERE rn = 1;
MODE() WITHIN GROUP (ORDER BY status) com GROUP BY user_id faz a mesma coisa em uma linha.
Diferencas entre motores
MODE() como agregado de conjunto ordenado e SQL padrao, suportado por PostgreSQL, Oracle e DB2. Mas varios motores populares nao o tem:
- MySQL e SQLite: nao ha funcao
MODE(). Use GROUP BY ... ORDER BY COUNT(*) DESC LIMIT 1 ou a variante com janela acima.
- ClickHouse: traz funcoes a parte —
topK(1)(col) retorna um array com o valor mais frequente, e anyHeavy(col) da uma moda aproximada sobre fluxos grandes.
SELECT topK(1)(status) AS top_status FROM orders;
Resumindo: quando seu banco suporta MODE() WITHIN GROUP, e a forma mais curta, legivel e deterministica de obter o valor mais frequente — sobretudo com GROUP BY, onde a alternativa incha numa subconsulta com funcao de janela.
MODE() WITHIN GROUP (ORDER BY col)retorna o valor mais frequente de uma coluna — a moda estatistica — em uma unica expressao limpa, sem subconsultas nem funcoes de janela. E um agregado de conjunto ordenado, e oORDER BYdecide qual valor vence quando as frequencias empatam.Sintaxe basica
A moda e o valor que aparece com mais frequencia. No PostgreSQL ela e escrita como um agregado de conjunto ordenado: tanto
WITHIN GROUPquantoORDER BYsao obrigatorios.-- Most frequent order status across the whole table SELECT MODE() WITHIN GROUP (ORDER BY status) AS top_status FROM orders; -- Most common country among registered users SELECT MODE() WITHIN GROUP (ORDER BY country) AS top_country FROM users;Vale lembrar:
ORDER BYaqui nao ordena o resultado; ele nomeia a coluna sobre a qual a moda e calculada e fornece a regra de desempate.NULLe ignorado na contagem de frequencias, como em qualquer agregado comum.Moda por grupo
Na maioria das vezes voce quer a moda por recorte, nao global: o status mais frequente de cada usuario, o departamento mais popular por gerente. Adicione
GROUP BY.-- Most frequent status per user SELECT user_id, MODE() WITHIN GROUP (ORDER BY status) AS usual_status FROM orders GROUP BY user_id; -- Dominant department per manager SELECT manager_id, MODE() WITHIN GROUP (ORDER BY dept) AS main_dept FROM employees GROUP BY manager_id;A expressao se le como uma unica coluna no
SELECT, entao outros agregados —COUNT(*),SUM(amount)e por ai vai — convivem ao lado dela sem joins extras.Resolucao de empates
O detalhe central: se varios valores compartilham a frequencia maxima,
MODE()retorna aquele que vem primeiro segundo oORDER BYinformado. Isso torna o resultado deterministico — ao contrario de umLIMIT 1sem ordenacao explicita.-- On a tie, the alphabetically smallest status wins SELECT MODE() WITHIN GROUP (ORDER BY status) AS top_status FROM orders; -- Force the tie to resolve toward the largest amount instead SELECT MODE() WITHIN GROUP (ORDER BY amount DESC) AS top_amount FROM orders;Pegadinha:
DESCnoORDER BYnao significa "o valor menos frequente". A moda continua sendo escolhida pela frequencia maxima;DESCso muda qual dos valores igualmente frequentes vence o empate. Achar um valor raro exige uma consulta totalmente diferente comCOUNT(*)ordenado de forma ascendente.Versus GROUP BY + COUNT + LIMIT 1
A forma classica de achar uma moda e contar frequencias e pegar a linha do topo:
-- Old-school: count, sort, take the top row SELECT status, COUNT(*) AS cnt FROM orders GROUP BY status ORDER BY cnt DESC LIMIT 1;Funciona, mas tem pontos fracos frente ao
MODE():LIMIT 1pega uma linha arbitraria a menos que voce adicione uma segunda chave de ordenacao — o resultado nao e deterministico.ROW_NUMBER() OVER (PARTITION BY ...)) ou a uma subconsulta correlacionada — bem mais codigo.SELECTsimples.O mesmo resultado "moda por usuario" com uma janela fica assim:
-- Per-group mode the hard way SELECT user_id, status AS usual_status FROM ( SELECT user_id, status, COUNT(*) AS cnt, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY COUNT(*) DESC, status) AS rn FROM orders GROUP BY user_id, status ) t WHERE rn = 1;MODE() WITHIN GROUP (ORDER BY status)comGROUP BY user_idfaz a mesma coisa em uma linha.Diferencas entre motores
MODE()como agregado de conjunto ordenado e SQL padrao, suportado por PostgreSQL, Oracle e DB2. Mas varios motores populares nao o tem:MODE(). UseGROUP BY ... ORDER BY COUNT(*) DESC LIMIT 1ou a variante com janela acima.topK(1)(col)retorna um array com o valor mais frequente, eanyHeavy(col)da uma moda aproximada sobre fluxos grandes.-- ClickHouse: most frequent value as a 1-element array SELECT topK(1)(status) AS top_status FROM orders;Resumindo: quando seu banco suporta
MODE() WITHIN GROUP, e a forma mais curta, legivel e deterministica de obter o valor mais frequente — sobretudo comGROUP BY, onde a alternativa incha numa subconsulta com funcao de janela.