Voltar para a lista de artigos Artigos
9 minutos de leitura

Como usar o SQL RANK OVER (PARTITION BY)

Classificar dados no SQL é muito fácil se você souber como usar RANK() para classificar em uma partição. Este artigo mostra como fazer isso e também como RANK() difere de DENSE_RANK() e ROW_NUMBER().

Se você trabalha com SQL em um ambiente profissional, já teve de classificar dados pelo menos uma vez. Pense em classificar meses/anos/trimestres por receita ou custos, produtos mais vendidos, publicações mais vistas ou músicas transmitidas, funcionários por salário, filiais mais lucrativas, etc.

Ou classificar livros por suas vendas.

O problema é que, muitas vezes, você precisa classificar os dados dentro de uma determinada categoria, ou partição, como chamamos. O SQL RANK OVER (PARTITION BY) entra no jogo!

Esse é um exemplo típico de funções de janela no SQL. Para obter uma explicação mais detalhada das funções de janela, nosso curso de funções de janela é o melhor. Por meio de 218 exercícios interativos, você aprenderá mais sobre PARTITION BY e outras cláusulas de função de janela, como ORDER BY, ROWS e RANGE. Essas funções são usadas não apenas na classificação, mas também em funções de janela agregadas e analíticas.

Vamos voltar às vendas de livros. Dê uma olhada nesta tabela, com o código para criá-la aqui. Como classificamos as vendas de livros em cada idioma?

idtitleauthororiginal_languagesalesclassify_under
1The HobbitJ. R. R. TolkienEnglish100Fantasy
2Watership DownRichard AdamsEnglish50Fantasy
3Harry Potter and the Philosopher's StoneJ. K. RowlingEnglish120Fantasy
4The PlagueAlbert CamusFrench12Classics
5The Divine ComedyDante AlighieriItalian12Poetry
6War and PeaceLeo TolstoyRussian36Classics
7Nineteen Eighty-FourGeorge OrwellEnglish30Classics
8Andromeda NebulaIvan YefremovRussian20Science fiction
9The Little PrinceAntoine de Saint-ExupéryFrench200Kids
10The StrangerAlbert CamusFrench10Classics
11The Adventures of PinocchioCarlo CollodiItalian35Kids
12The Name of the RoseUmberto EcoItalian50Classics
13One Hundred Years of SolitudeGabriel García MárquezSpanish50Classics
14Don QuixoteMiguel de CervantesSpanish500Classics
15LolitaVladimir NabokovEnglish50Classics

A solução é simples quando você sabe que o idioma, neste exemplo, atua como algo chamado partição de dados.

SELECT
  original_language,
  title,
  author,
  sales,
  RANK() OVER (PARTITION BY original_language ORDER BY sales DESC)
    AS sales_rank
FROM books;

E aqui está ela!

original_languagetitleauthorsalessales_rank
EnglishHarry Potter and the Philosopher's StoneJ. K. Rowling1201
EnglishThe HobbitJ. R. R. Tolkien1002
EnglishLolitaVladimir Nabokov503
EnglishWatership DownRichard Adams503
EnglishNineteen Eighty-FourGeorge Orwell305
FrenchThe Little PrinceAntoine de Saint-Exupéry2001
FrenchThe PlagueAlbert Camus122
FrenchThe StrangerAlbert Camus103
ItalianThe Name of the RoseUmberto Eco501
ItalianThe Adventures of PinocchioCarlo Collodi352
ItalianThe Divine ComedyDante Alighieri123
RussianWar and PeaceLeo Tolstoy361
RussianAndromeda NebulaIvan Yefremov202
SpanishDon QuixoteMiguel de Cervantes5001
SpanishOne Hundred Years of SolitudeGabriel García Márquez502

Agora, vamos dissecar esse exemplo.

O que é RANK()?

Em termos mais simples, RANK() é uma função de janela.

As funções de janela do SQL são semelhantes às funções de agregação, pois são aplicadas a um grupo de linhas. Uma grande diferença: as funções de janela mantêm os detalhes das linhas individuais, ao contrário das funções de agregação com GROUP BY.

O que RANK() faz?

Como o nome sugere, ela classifica os dados. Isso a torna uma função de janela de classificação, juntamente com DENSE_RANK() e ROW_NUMBER().

Ao usar qualquer uma dessas funções de janela, ela deve ser acompanhada por uma cláusula OVER (ORDER BY). A cláusula OVER() é obrigatória para qualquer função de janela. É ela que transforma uma função "regular" em uma função de janela.

Para essas funções de classificação, o ORDER BY entre parênteses define a ordem em que a classificação é feita. Essa ordem pode ser ascendente ou descendente. Lembre-se de que isso não afeta a ordem das linhas no resultado; isso é feito com um ORDER BY no final da consulta.

Saiba mais sobre isso em nosso artigo sobre a função de janela RANK().

Como RANK() funciona com OVER (ORDER BY)

Vamos usar o conjunto de dados acima. Pegue a tabela inteira e classifique os livros por vendas. Vamos ver o que acontece.

SELECT
  title,
  author,
  sales,
  RANK() OVER (ORDER BY sales DESC) AS sales_rank
FROM books;

Essa é a mesma consulta anterior, exceto pelo fato de que ela não seleciona o idioma e não usa PARTITION BY. Portanto, a função RANK() é seguida por OVER(). A cláusula ORDER BY informa à função para classificar os dados por vendas em ordem decrescente, ou seja, dos livros mais vendidos para os menos vendidos. Como a cláusula PARTITION BY é omitida, a função classifica a tabela inteira.

Aqui estão as primeiras dez linhas do resultado.

titleauthorsalessales_rank
Don QuixoteMiguel de Cervantes5001
The Little PrinceAntoine de Saint-Exupéry2002
Harry Potter and the Philosopher's StoneJ. K. Rowling1203
The HobbitJ. R. R. Tolkien1004
LolitaVladimir Nabokov505
Watership DownRichard Adams505
The Name of the RoseUmberto Eco505
One Hundred Years of SolitudeGabriel García Márquez505
War and PeaceLeo Tolstoy369
The Adventures of PinocchioCarlo Collodi3510

O ponto crucial a ser observado aqui: há quatro livros com 50 milhões de cópias vendidas, e todos estão em quinto lugar.

É assim que a RANK() difere das outras duas funções de janela de classificação - ela atribui a mesma classificação a valores empatados. Quando a função atinge o próximo valor de vendas (nesse caso, 36 milhões de cópias vendidas), ela não atribui o próximo valor de classificação consecutivo (6), mas pula para ajustar a contagem de valores de vendas empatados. Conforme mencionado, a classificação "5" aparece quatro vezes; portanto, a próxima classificação atribuída é nove.

DENSE_RANK() também classifica os empates com a mesma classificação. Entretanto, ao contrário do RANK(), ele não pula os valores de classificação com base nos empates. Os mesmos dados classificados com DENSE_RANK() são os seguintes.

titleauthorsalessales_rank
Don QuixoteMiguel de Cervantes5001
The Little PrinceAntoine de Saint-Exupéry2002
Harry Potter and the Philosopher's StoneJ. K. Rowling1203
The HobbitJ. R. R. Tolkien1004
LolitaVladimir Nabokov505
Watership DownRichard Adams505
The Name of the RoseUmberto Eco505
One Hundred Years of SolitudeGabriel García Márquez505
War and PeaceLeo Tolstoy366
The Adventures of PinocchioCarlo Collodi357

Depois de vários livros classificados em quinto lugar, a próxima classificação é a sexta, e não a nona, como em RANK().

E quanto ao ROW_NUMBER()? Ele não se preocupa com empates ou saltos. Ele apenas classifica as linhas sequencialmente. As dez primeiras linhas usando ROW_NUMBER() estão abaixo.

titleauthorsalessales_rank
Don QuixoteMiguel de Cervantes5001
The Little PrinceAntoine de Saint-Exupéry2002
Harry Potter and the Philosopher's StoneJ. K. Rowling1203
The HobbitJ. R. R. Tolkien1004
LolitaVladimir Nabokov505
Watership DownRichard Adams506
The Name of the RoseUmberto Eco507
One Hundred Years of SolitudeGabriel García Márquez508
War and PeaceLeo Tolstoy369
The Adventures of PinocchioCarlo Collodi3510

As diferenças entre essas funções são explicadas na visão geral das funções de classificação.

Como RANK() funciona com OVER (PARTITION BY)

A cláusula PARTITION BY divide os dados em partições ou subconjuntos. Quando usada com RANK(), isso significa que os dados são classificados dentro da partição. Quando atingem a segunda partição, a classificação é redefinida para começar a partir da primeira.

Vamos examinar novamente a consulta do início deste artigo para esclarecer isso.

SELECT
  original_language,
  title,
  author,
  sales,
  RANK() OVER (PARTITION BY original_language ORDER BY sales DESC)
    AS sales_rank
FROM books;

Nessa consulta, RANK() é usado com PARTITION BY. A partição, nesse caso, é original_language. A classificação é feita por vendas em ordem decrescente, conforme especificado na cláusula ORDER BY.

A forma como escrevemos PARTITION BY e ORDER BY significa que os livros são classificados por vendas, mas dentro de cada categoria de idioma. Depois que a função classifica todos os livros em um idioma, ela reinicia quando chega ao segundo idioma, e assim por diante.

Vemos isso no resultado da consulta.

original_languagetitleauthorsalessales_rank
EnglishHarry Potter and the Philosopher's StoneJ. K. Rowling1201
EnglishThe HobbitJ. R. R. Tolkien1002
EnglishLolitaVladimir Nabokov503
EnglishWatership DownRichard Adams503
EnglishNineteen Eighty-FourGeorge Orwell305
FrenchThe Little PrinceAntoine de Saint-Exupéry2001
FrenchThe PlagueAlbert Camus122
FrenchThe StrangerAlbert Camus103
ItalianThe Name of the RoseUmberto Eco501
ItalianThe Adventures of PinocchioCarlo Collodi352
ItalianThe Divine ComedyDante Alighieri123
RussianWar and PeaceLeo Tolstoy361
RussianAndromeda NebulaIvan Yefremov202
SpanishDon QuixoteMiguel de Cervantes5001
SpanishOne Hundred Years of SolitudeGabriel García Márquez502

Marcamos cada partição com uma cor diferente para facilitar a visualização das diferentes partições. O livro mais vendido em inglês é Harry Potter e a Pedra Filosofal, de J.K. Rowling. Em seguida, vem O Hobbit. Lolita e Watership Down estão ambos em terceiro lugar, pois o site RANK() atribui a mesma classificação a livros com o mesmo valor de vendas. Em seguida, um valor de classificação é pulado, e Nineteen Eighty-Four é classificado em quinto lugar.

A próxima partição é o idioma francês, e a classificação é reiniciada. O Pequeno Príncipe é classificado como o livro mais vendido em francês.

Vemos que a mesma lógica funciona para livros em italiano, russo e espanhol.

Exemplo de bônus

Estudamos essas consultas para mostrar como o RANK() OVER (PARTITION BY) funciona. Agora, vamos praticar!

O exemplo a seguir não é muito diferente da primeira consulta. Você não deve ter problemas para aplicar o que aprendeu.

Há uma coluna na tabela books chamada classify_under. Ela especifica a categoria na qual cada livro deve ser colocado na livraria.

Vamos classificar os livros por vendas para cada categoria.

SELECT
  classify_under,
  title,
  author,
  sales,
  RANK() OVER (PARTITION BY classify_under ORDER BY sales DESC)
    AS sales_rank
FROM books;

A consulta seleciona a coluna classify_under em vez de original_language na primeira consulta. Todas as outras colunas selecionadas são as mesmas.

Há também uma diferença em RANK(). Como estamos classificando por classify_under, essa é a coluna que deve estar na cláusula PARTITION BY.

Mais uma vez, queremos classificar os livros por vendas em ordem decrescente. Aqui está a classificação:

classify_undertitleauthorsalessales_rank
ClassicsDon QuixoteMiguel de Cervantes5001
ClassicsLolitaVladimir Nabokov502
ClassicsThe Name of the RoseUmberto Eco502
ClassicsOne Hundred Years of SolitudeGabriel García Márquez502
ClassicsWar and PeaceLeo Tolstoy365
ClassicsNineteen Eighty-FourGeorge Orwell306
ClassicsThe PlagueAlbert Camus127
ClassicsThe StrangerAlbert Camus108
FantasyHarry Potter and the Philosopher's StoneJ. K. Rowling1201
FantasyThe HobbitJ. R. R. Tolkien1002
FantasyWatership DownRichard Adams503
KidsThe Little PrinceAntoine de Saint-Exupéry2001
KidsThe Adventures of PinocchioCarlo Collodi352
PoetryThe Divine ComedyDante Alighieri121
Science fictionAndromeda NebulaIvan Yefremov201

Por pura coincidência, há cinco partições novamente. Na categoria "Clássicos", Dom Quixote é o livro mais vendido. Em seguida, há três livros classificados em segundo lugar. A sequência de classificação é pulada até chegarmos a Guerra e Paz, em quinto lugar. O restante dos clássicos é classificado sequencialmente, pois não há mais empates.

Em outras categorias, não há empates. "Poesia" e "Ficção científica" têm apenas um livro em cada categoria. Portanto, há apenas a primeira classificação.

Para obter mais exemplos com outras funções de janela, consulte nosso artigo que explica como usar PARTITION BY.

Adicione RANK() ao seu vocabulário SQL

Vimos os usos mais comuns da função de janela RANK(). Embora ela exija uma cláusula OVER (ORDER BY), a cláusula PARTITION BY abre suas possibilidades. Ela torna o RANK() uma ferramenta sofisticada para classificar dados em uma ou mais partições com facilidade em seu trabalho diário.

Para saber mais e praticar RANK() e outras funções de janela (de classificação), use nosso Funções de Janela (Window Functions) em SQL curso. Você terá uma explicação ainda mais detalhada da classificação e a chance de escrever muitos códigos em nossos exercícios.

Boa classificação!