Voltar para a lista de artigos Artigos
16 minutos de leitura

Como classificar as linhas em SQL: Um Guia Completo

Não tenha mais dificuldades com as funções de ranking SQL! Este artigo irá guiá-lo através dos casos de uso mais comuns da classificação.

Os rankings são usados ao nosso redor todos os dias. Torneios esportivos, os melhores filmes e séries de TV na Netflix, lojas com os produtos mais baratos - estes são apenas alguns exemplos dos rankings que você pode ter visto recentemente.

O uso das funções do SQL RANK pode ser difícil às vezes. A variedade de construções diferentes é enorme. Você pode facilmente se perder em tudo o que há para aprender sobre classificação. A chave é entender os conceitos importantes e saber onde procurar por informações adicionais.

Neste guia, você encontrará o conhecimento que lhe permitirá escrever muitos tipos de consultas de classificação SQL. Primeiro, vou explicar como funciona a classificação. Em seguida, mostrarei uma série de exemplos. Se você estiver procurando por casos de uso específico, você pode simplesmente mergulhar na seção de casos de uso do Ranking SQL.

As funções do Ranking SQL são Funções de Janela (Window Functions) em SQL

Vamos começar com o básico. Quais são exatamente as funções de ranking em SQL? Elas fazem parte de uma família de funções chamada funções de janela. As funções de janela utilizam um escopo (janela), que olha parte dos dados para computar o resultado. Em seguida, ela se move para outra parte dos dados e calcula o resultado para essa parte. Você pode obter uma idéia básica de como funciona a partir do exemplo de funções de janela SQL com explicações.

Embora existam muitas funções de janela diferentes, vou me concentrar apenas na classificação. Também não vou explicar profundamente o funcionamento interno das funções de janela. Este artigo focalizará as funções de ranking SQL e como utilizá-las em diferentes situações. Para saber mais sobre funções de janela, verifique isto Funções de Janela (Window Functions) em SQL curso.

Funções Básicas de Ranking

Os tipos mais comuns (e simples) de funções de classificação são:

  • RANK()
  • DENSE_RANK()
  • ROW_NUMBER()

O que eles fazem e como são diferentes um do outro? Vou explicar brevemente cada um deles. Depois, vou comparar as classificações de cada uma das funções.

A função RANK() cria uma classificação das linhas com base em uma coluna fornecida. Ela começa com a atribuição de "1" à primeira linha na ordem e depois dá números mais altos às linhas mais baixas na ordem. Se as linhas tiverem o mesmo valor, elas são classificadas da mesma forma. Entretanto, o próximo ponto é deslocado de acordo. Por exemplo, se duas fileiras tiverem o mesmo valor, a próxima fileira será a sétima (ou seja, a sexta não existe).

A função DENSE_RANK() é bastante semelhante. A única diferença é que ela não deixa lacunas no ranking. Mesmo que mais de uma fileira possa ter a mesma classificação, a próxima fileira terá a próxima classificação. Por exemplo, se duas fileiras forem a 5ª, a próxima fileira será a 6ª.

A função ROW_NUMBER() é diferente. Se as linhas tiverem o mesmo valor, todas elas receberão classificações consecutivas (não a mesma classificação que as funções anteriores). Por exemplo, se duas linhas tiverem o mesmo valor (ambas seriam a 5ª com as funções anteriores), ROW_NUMBER() as colocaria na 5ª e 6ª posições.

Agora, você pode se perguntar: Se algumas linhas têm o mesmo valor e estamos usando ROW_NUMBER(), como podemos dizer qual linha será a primeira, segunda, etc.? A resposta é um pouco sombria: não podemos! Depende de muitos fatores, e você não pode prever qual fileira terá qual classificação.

Estas são as funções básicas e (provavelmente) as mais comumente usadas no ranking SQL. Se você quiser aprender sobre outras funções de ranking, você pode ler esta visão geral das funções de ranking.

RANK() vs. DENSE_RANK() vs. ROW_NUMBER()

Agora que você conhece a teoria das funções de ranking, vamos comparar os resultados destas funções. Para fazer isso, vamos classificar algumas pessoas com base em seus resultados dos testes. Dê uma olhada nos dados (tabela exam_result):

first_namelast_namepoints
EmyrDownes70
DinaMorin70
Evie-MayBoyer80
NoraParkinson60
TrystanOconnor40
ErykMyers90
MarleneDuncan90
MariusPowell90
JoanneGoddard50
RayhanWilliamson90

Vamos criar uma consulta que classifica as linhas pela coluna de pontos usando as funções de classificação descritas acima:

SELECT
  RANK() OVER(ORDER BY points DESC) AS rank,
  DENSE_RANK() OVER(ORDER BY points DESC) AS dense_rank,
  ROW_NUMBER() OVER(ORDER BY points DESC) AS row_number,
  first_name,
  last_name,
  points
FROM exam_result;

Vamos analisar o código antes de executá-lo para ver os resultados. A primeira coluna que queremos mostrar é rank. Será simplesmente o ranking criado usando a função RANK(). Entretanto, o que significa OVER(ORDER BY points DESC)? É parte do conceito de função de janela. OVER() é uma parte obrigatória de todas as funções de ranking (na verdade, todas as funções de janela). Ela informa a função sobre o escopo dos dados (janela) e a ordem em que as linhas serão colocadas. Neste caso, nós apenas fornecemos a ordem. Você pode encontrar um exemplo de definição de uma janela na seção RANK() OVER(PARTITION BY ...)-Coluna Única section.

Como você agora entende a primeira coluna, você também deve entender as duas seguintes. A única diferença é o nome das funções de classificação. Além disso, as três últimas colunas devem ser auto-explicativas, como você já viu os dados.

Muito bem, vamos dar uma olhada nos resultados:

rankdense_rankrow_numberfirst_namelast_namepoints
111MarleneDuncan90
112RayhanWilliamson90
113MariusPowell90
114ErykMyers90
525Evie-MayBoyer80
636EmyrDownes70
637DinaMorin70
848NoraParkinson60
959JoanneGoddard50
10610TrystanOconnor40

Você consegue identificar as diferenças entre cada função? Para as primeiras linhas, a classificação e as colunas dense_rank parecem as mesmas. No entanto, row_number parece diferente desde o início. Mesmo que Marlene, Rayhan, Marius e Eryk tenham a mesma quantidade de pontos, a função ROW_NUMBER() lhes atribuiu valores distintos. Como mencionado anteriormente, a ordem em que estas pessoas obtiveram sua classificação é não-determinista.

Vale notar que você pode tornar o resultado de ROW_NUMBER() mais determinístico. Basta adicionar mais colunas à ordem (por exemplo, ROW_NUMBER() OVER (ORDER BY points DESC, last_name ASC)). Isto será explicado com mais detalhes na seção Rank Over Multiple Columns.

Você também pode tornar o ROW_NUMBER() ainda menos determinístico! Mesmo que a parte ORDER BY seja uma necessidade para RANK() e DENSE_RANK(), ROW_NUMBER() não o exige de forma alguma. É assim que você pode numerar as filas sem nenhuma ordem específica.

Outra diferença pode ser vista mais abaixo na tabela. Evie-May é classificado em 5º lugar pela função RANK() e em 2º pela função DENSE_RANK(). A função anterior observa quantas linhas tinham a mesma classificação e a linha seguinte é classificada de acordo. A última função simplesmente olha para o valor anterior e a próxima linha é dada a classificação seguinte.

Tome um momento para olhar as classificações acima e certifique-se de entender a diferença entre estas três funções de classificação SQL. É importante saber qual a função a ser usada em quais casos. Se você precisar de mais explicações, verifique Como Usar Funções de Ranking em SQL.

Casos de Uso do Ranking SQL

É hora de mergulhar em algumas consultas ao SQL RANK. Vamos começar com algumas consultas simples e aumentar gradualmente a complexidade. O plano é o seguinte:

Note que mesmo que usaremos principalmente a função SQL RANK(), os exemplos podem ser aplicados a DENSE_RANK() e ROW_NUMBER(). Depende de suas necessidades. Portanto, é importante entender as diferenças entre estas funções.

Nos exemplos, vamos utilizar dados modificados do exemplo anterior. A coluna exam_date mostra quando a pessoa fez o exame, e a coluna da cidade mostra em que city a pessoa fez o exame. Dê uma olhada:

first_namelast_nameexam_datecitypoints
EmyrDownes2018-12-18San Francisco70
DinaMorin2019-01-17Los Angeles70
Evie-MayBoyer2019-01-23San Francisco80
NoraParkinson2019-02-16San Diego60
TrystanOconnor2019-02-28Los Angeles40
ErykMyers2019-06-07San Francisco90
MarleneDuncan2019-06-13San Diego90
MariusPowell2019-11-13San Diego90
JoanneGoddard2019-12-18San Diego50
MariusWilliamson2020-01-02San Diego90

Rank Over Coluna Única

Vamos começar com o caso mais simples de uso: criar um ranking baseado em apenas uma coluna. Vamos basear nosso ranking em pontos:

SELECT
  RANK() OVER(ORDER BY points DESC) AS ranking,
  first_name,
  last_name,
  points
FROM exam_result;

A consulta é bastante simples, como no exemplo anterior. Ela cria um ranking com base no número de pontos em ordem decrescente. Estamos usando RANK() OVER(ORDER BY ...) para indicar qual coluna deve ser usada para o pedido. Os resultados são parecidos com estes:

rankingfirst_namelast_namepoints
1MarleneDuncan90
1MariusWilliamson90
1MariusPowell90
1ErykMyers90
5Evie-MayBoyer80
6EmyrDownes70
6DinaMorin70
8NoraParkinson60
9JoanneGoddard50
10TrystanOconnor40

Se você quiser criar um ranking baseado em uma coluna diferente ou em uma ordem diferente (isto é, ascendente ao invés de descendente), basta mudar o nome da coluna ou mudar a palavra-chave DESC para a palavra-chave ASC.

Rank Sobre Múltiplas Colunas

O uso de várias colunas para pedidos também é simples. Basta adicionar o nome da próxima coluna após a vírgula. Se os valores na primeira coluna forem iguais, então a segunda coluna é levada em consideração, e assim por diante. Dê uma olhada:

SELECT
  RANK() OVER(ORDER BY points DESC, first_name ASC) AS ranking,
  first_name,
  last_name,
  points
FROM exam_result;

Esta consulta é semelhante à anterior. Acrescentamos first_name ASC à cláusula de pedido. Desta forma, se algumas linhas tiverem o mesmo valor na primeira coluna, a segunda coluna é levada em consideração. Note que, se a segunda coluna for a mesma, então a classificação é resolvida com base na função que estamos usando (em nosso caso RANK()). Dê uma olhada no resultado:

rankingfirst_namelast_namepoints
1ErykMyers90
2MariusWilliamson90
2MariusPowell90
4MarleneDuncan90
5Evie-MayBoyer80
6DinaMorin70
7EmyrDownes70
8NoraParkinson60
9JoanneGoddard50
10TrystanOconnor40

Como você pode ver, pessoas com o mesmo número de pontos recebem classificações diferentes porque têm nomes diferentes. As únicas exceções são as duas pessoas chamadas Marius. Por terem o mesmo nome e o mesmo número de pontos, elas têm a mesma classificação.

Vale a pena mencionar que a ordenação por algumas colunas pode ser útil se você quiser tornar o resultado da função ROW_NUMBER() determinista. Dê uma olhada nesta consulta:

SELECT
  ROW_NUMBER() OVER(ORDER BY points DESC, last_name ASC) AS ranking,
  first_name,
  last_name,
  points
FROM exam_result;

Como todos em nossos dados têm um sobrenome diferente, podemos adicioná-lo à cláusula de ordenação para que possamos prever quem recebe qual classificação. Dê uma olhada:

rankingfirst_namelast_namepoints
1MarleneDuncan90
2ErykMyers90
3MariusPowell90
4MariusWilliamson90
5Evie-MayBoyer80
6EmyrDownes70
7DinaMorin70
8NoraParkinson60
9JoanneGoddard50
10TrystanOconnor40

Neste caso, a função ROW_NUMBER() funciona como RANK() e DENSE_RANK() porque cada linha pode ser colocada deterministicamente. Entretanto, observe que se houvesse pessoas com o mesmo sobrenome, a função ROW_NUMBER() não seria determinística, mesmo que estejamos ordenando por duas colunas.

RANK() Com os 10 melhores resultados

Estamos freqüentemente criando rankings para mostrar os melhores resultados (por exemplo, top 10, top 100, etc.). Como você pode esperar, você pode mostrar os melhores resultados usando as funções de ranking SQL. No entanto, tal consulta é um pouco mais complicada. Dê uma olhada:

SELECT
  *
FROM (
  SELECT
    RANK() OVER(ORDER BY points DESC) AS ranking,
    first_name,
    last_name,
    points
  FROM exam_result
) AS a
WHERE ranking <= 3;

Nesta consulta, usamos uma subconsulta para calcular a classificação, e então, na consulta principal, filtramos os resultados para que apenas os três primeiros sejam mostrados. Os resultados podem surpreendê-lo:

rankingfirst_namelast_namepoints
1MarleneDuncan90
1MariusWilliamson90
1MariusPowell90
1ErykMyers90

Como você pode ver, os resultados mostram quatro linhas, e cada uma delas tem o mesmo valor na coluna de classificação. É por causa de nossos dados. Quatro filas têm o mesmo número de pontos e, portanto, recebem o mesmo valor na subconsulta.

Para escrever tal consulta por conta própria, coloque sua consulta de classificação dentro de uma subconsulta e escreva filtrando em torno dela. Por exemplo, para mostrar o 10, mude WHERE ranking <= 3 para WHERE ranking <= 10.

Também vale a pena notar o que acontece quando você usa DENSE_RANK() com estes dados. Dê uma olhada na consulta abaixo e em seus resultados:

SELECT
  *
FROM (
  SELECT
    DENSE_RANK() OVER(ORDER BY points DESC) AS ranking,
    first_name,
    last_name,
    points
  FROM exam_result
) AS a
WHERE ranking <= 3;
rankingfirst_namelast_namepoints
1MarleneDuncan90
1MariusWilliamson90
1MariusPowell90
1ErykMyers90
2Evie-MayBoyer80
3EmyrDownes70
3DinaMorin70

Como você pode ver, há muitas filas no resultado! Mais uma vez, é por causa de como DENSE_RANK() classifica cada fileira. Este exemplo mostra claramente que é importante entender a diferença entre cada função e saber qual usar em uma determinada situação.

Ranking por data

SELECT
  RANK() OVER(ORDER BY exam_date ASC) AS ranking,
  first_name,
  last_name,
  exam_date
FROM exam_result;

Neste caso, estamos simplesmente usando a coluna exam_date para fazer o pedido. Há muito pouca diferença entre encomendar por data e por qualquer outra coluna. Dê uma olhada nos resultados:

rankingfirst_namelast_nameexam_date
1EmyrDownes2018-12-18
2DinaMorin2019-01-17
3Evie-MayBoyer2019-01-23
4NoraParkinson2019-02-16
5TrystanOconnor2019-02-28
6ErykMyers2019-06-07
7MarleneDuncan2019-06-13
8MariusPowell2019-11-13
9JoanneGoddard2019-12-18
10MariusWilliamson2020-01-02

O importante a lembrar é que ASC (ascendente) em caso de datas significa que a mais antiga será colocada em primeiro lugar. Na ordem DESC (descendente), a mais nova data será colocada em primeiro lugar.

Ranking por mês

Você também pode querer encomendar as filas usando apenas uma parte da data (por exemplo, mês). Não é tão difícil se você souber as funções da data. Dê uma olhada na consulta a seguir:

SELECT
  RANK() OVER(ORDER BY EXTRACT('year' FROM exam_date) ASC,
    EXTRACT('month' FROM exam_date) ASC) AS ranking,
  first_name,
  last_name,
  exam_date
FROM exam_result;

Estamos usando a função EXTRACT() para extrair uma determinada parte da data. Primeiro, queremos ordenar as filas por ano e depois por mês. Portanto, estamos usando primeiro EXTRACT('year' FROM exam_date) e depois EXTRACT('month' FROM exam_date). Dê uma olhada nos resultados:

rankingfirst_namelast_nameexam_date
1EmyrDownes2018-12-18
2Evie-MayBoyer2019-01-23
2DinaMorin2019-01-17
4NoraParkinson2019-02-16
4TrystanOconnor2019-02-28
6MarleneDuncan2019-06-13
6ErykMyers2019-06-07
8MariusPowell2019-11-13
9JoanneGoddard2019-12-18
10MariusWilliamson2020-01-02

EXTRACT() é uma função bastante simples. Você diz qual parte da data você quer extrair da data. Há muitas partes de datas diferentes que você pode usar. É melhor consultar a documentação do seu sistema de gerenciamento de banco de dados (SGBD). As partes mais comuns são:

  • 'year'
  • 'month'
  • 'day'
  • 'hour'
  • 'minute'
  • 'second'

Também vale a pena mencionar que nem todos os SGBD suportam esta função. As que incluem o PostgreSQL e o MySQL. No SQL Server, esta função é chamada DATEPART().

As operações de data e hora são um tópico bastante difícil, que está além do escopo deste artigo. Se você gostaria de saber mais sobre a função EXTRACT() e muito mais, confira a Funções Comuns em SQL curso. Possui uma seção inteira dedicada às operações de data e hora.

Ranking com GROUP BY

Agora, vamos dar uma olhada em como usar os rankings com funções agregadas. Mesmo que pareça assustador, com a devida compreensão, é um conceito bastante lógico. Seu banco de dados calcula primeiro as funções agregadas e depois cria uma classificação baseada nos valores computados. Dê uma olhada neste exemplo com AVG():

SELECT
  RANK() OVER(ORDER BY AVG(points) DESC) AS ranking,
  city,
  AVG(points) AS average_points
FROM exam_result
GROUP BY city;

Como você pode ver, esta consulta não é muito diferente das outras consultas que vimos até agora. Você pode simplesmente usar funções agregadas dentro das funções de ranking. O importante a lembrar é usar a cláusula GROUP BY. Como mencionado acima, as funções agregadas são computadas primeiro. Isto significa que com GROUP BY, você só pode usar funções agregadas ou as expressões que você está agrupando dentro da função de ranking.

Por exemplo, se você quiser usar outra coluna para ordenação, de modo que as linhas sejam ordenadas por esta outra coluna se o número médio de pontos for o mesmo, você teria que incluir esta outra coluna na cláusula GROUP BY.

A consulta acima retorna o número médio de pontos marcados pelas pessoas de cada cidade.

rankingcityaverage_points
1San Francisco80
2San Diego76
3Los Angeles55

Ranking com COUNT()

Agora, vamos dar uma olhada em uma função agregada frequentemente utilizada -COUNT():

SELECT
  RANK() OVER(ORDER BY COUNT(*) DESC) AS ranking,
  city,
  COUNT(*) AS exam_takers
FROM exam_result
GROUP BY city;

Como você pode ver, esta consulta não é muito diferente da consulta anterior. Todas as funções agregadas são utilizadas com funções de ranking da mesma forma.

Os resultados são os seguintes:

rankingcityexam_takers
1San Diego5
2San Francisco3
3Los Angeles2

A consulta calcula o número de pessoas que fizeram o exame em cada cidade e depois cria uma classificação com base neste valor.

Ranking com SUM()

Outra função agregada popular é SUM(). É também bastante simples de usar:

SELECT
  RANK() OVER(ORDER BY SUM(points) DESC) AS ranking,
  city,
  SUM(points) AS total_points
FROM exam_result
GROUP BY city;

Mais uma vez, estamos simplesmente usando a função SUM() com a função SQL RANK().

rankingcitytotal_points
1San Diego380
2San Francisco240
3Los Angeles110

Desta vez, calculamos o número total de pontos adquiridos pelas pessoas de cada cidade.

RANK() OVER(PARTITION BY ...)- Coluna Única

Espero que você esteja pronto para enfrentar um conceito de classificação mais avançado: PARTITION BY. Ele permite que você crie classificações em grupos separados. Dê uma olhada nesta consulta:

SELECT
  RANK() OVER(PARTITION BY city ORDER BY points DESC) AS ranking,
  city,
  first_name,
  last_name,
  points
FROM exam_result;

Nesta consulta, estamos usando PARTITION BY com uma única coluna para criar um ranking de pessoas em cada cidade. Desta forma, podemos ver as pessoas com as mais altas pontuações em cada cidade. Dê uma olhada nos resultados:

rankingcityfirst_namelast_namepoints
1San FranciscoErykMyers90
2San FranciscoEvie-MayBoyer80
3San FranciscoEmyrDowes70
1Los AngelesDinaMorin70
2Los AngelesTrystanOconnor40
1San DiegoMarleneDuncan90
1San DiegoMariusPowell90
1San DiegoMariusWilliamson90
4San DiegoNoraParkinson60
5San DiegoJoanneGoddard50

Como você pode ver, os rankings são computados separadamente para cada cidade. Desta forma, Eryk e Dina obtêm a mesma classificação, apesar de terem pontuações diferentes. Além disso, Dina e Emyr têm as mesmas notas, mas Dina é classificada mais alta porque ela fez o exame em uma cidade diferente.

Em alguns casos, a PARTITION BY é um conceito importante e, portanto, vale a pena lembrar. O bom é que também é bastante simples de usar. Entretanto, se você quiser mais informações, talvez queira ler Common SQL Funções de Janela (Window Functions) em SQL: Utilização de Partições com Funções de Ranking no blog LearnSQL.com.br .

RANK() OVER(PARTITION BY ...)-Multiple Columns

Finalmente, vejamos a cláusula PARTITION BY com várias colunas. Não é muito diferente de usar PARTITION BY com apenas uma coluna. Dê uma olhada:

SELECT
  RANK() OVER(PARTITION BY city, first_name
    ORDER BY exam_date ASC) AS ranking,
  city,
  first_name,
  last_name,
  exam_date
FROM exam_result;

Na consulta acima, estamos usando PARTITION BY com duas colunas: city e first_name. Isto significa que dentro de cada par distinto de cidade e primeiro nome, teremos classificações separadas. Dê uma olhada nos resultados:

rankingcityfirst_namelast_nameexam_date
1San FranciscoErykMyers2019-06-07
1San FranciscoEvie-MayBoyer2019-01-23
1San FranciscoEmyrDowes2018-12-18
1Los AngelesDinaMorin2019-01-17
1Los AngelesTrystanOconnor2019-02-28
1San DiegoMarleneDuncan2019-06-13
1San DiegoMariusPowell2019-11-13
2San DiegoMariusWilliamson2020-01-02
1San DiegoNoraParkinson2019-02-16
1San DiegoJoanneGoddard2019-12-18

Como você pode ver acima, a maioria das pessoas está classificada em primeiro lugar. Isto porque a maioria dos pares (cidade e primeiro nome) são únicos. No entanto, há um par que não é único. Há duas pessoas de San Diego chamadas Marius. Marius Powell é o primeiro porque ele fez o exame antes de Marius Williamson.

Lembre-se, a prática torna perfeito

Como você pode ver, há inúmeros casos de uso para funções de ranking em SQL. Portanto, é importante conhecê-los bem, você provavelmente terá que escrever uma consulta de ranking SQL mais cedo ou mais tarde.

A melhor maneira de aprender sobre funções de classificação (e funções de janela em geral) é através da prática. Eu recomendo isto Funções de Janela (Window Functions) em SQL curso. Possui 218 exercícios interativos, o que equivale a cerca de 20 horas de codificação. Isso é muito, especialmente se você decidir fazer isso de uma só vez. Nós não recomendamos isto. É melhor divulgar seus estudos ao longo de vários dias. Aqui você encontrará mais dicas sobre como se manter saudável ao aprender SQL. Cuide de seu corpo enquanto desenvolve sua carreira, e comece a aprender SQL hoje mesmo.