Problema:
Você agrupou seus dados com
GROUP BY
e gostaria de exibir apenas a primeira linha de cada grupo. Exemplo:
Nosso banco de dados tem uma tabela chamada
exam_results
com os dados da tabela a seguir:first_name | last_name | ano | resultado |
---|---|---|---|
João | Klein | 2020 | 40 |
Editar | Preto | 2020 | 43 |
Marcar | Johnson | 2019 | 32 |
Laura | Verão | 2020 | 35 |
Kate | Smith | 2019 | 41 |
Jacob | Preto | 2019 | 44 |
Tom | Bennet | 2020 | 38 |
Emily | Kelly | 2020 | 43 |
Para cada ano, vamos encontrar o aluno com o melhor
result
. Se houver dois alunos empatados como o melhor em um grupo, selecionaremos arbitrariamente um deles para exibir. Solução:
WITH added_row_number AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number FROM exam_results ) SELECT * FROM added_row_number WHERE row_number = 1;
O resultado é:
first_name | last_name | ano | resultado | número_linha |
---|---|---|---|---|
Jacob | Preto | 2019 | 44 | 1 |
Emily | Kelly | 2020 | 43 | 1 |
Discussão:
Primeiro, você precisa escrever um CTE no qual você atribui um número a cada linha dentro de cada grupo. Para fazer isso, você pode usar o
ROW_NUMBER()
função. Em OVER()
, você especifica os grupos em que as linhas devem ser divididas (PARTITION BY
) e a ordem em que os números devem ser atribuídos às linhas (ORDER BY
). Dê uma olhada no resultado da consulta interna:
SELECT *, ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number FROM exam_results;
first_name | last_name | ano | resultado | número_linha |
---|---|---|---|---|
Jacob | Preto | 2019 | 44 | 1 |
Kate | Smith | 2019 | 41 | 2 |
Marcar | Johnson | 2019 | 32 | 3 |
Emily | Kelly | 2020 | 43 | 1 |
Editar | Preto | 2020 | 43 | 2 |
João | Klein | 2020 | 40 | 3 |
Tom | Bennet | 2020 | 38 | 4 |
Laura | Verão | 2020 | 35 | 5 |
Você atribui os números de linha dentro de cada grupo (ou seja, ano). Cada linha tem um número de linha baseado no valor do
result
coluna. As linhas são classificadas em ordem decrescente devido ao DESC
palavra-chave após ORDER BY result
. Mesmo se houver várias linhas em um grupo que tenham o mesmo valor de result
, as linhas ainda recebem números diferentes. Aqui, Edith Black e Emily Kelly têm o mesmo result
mas números de linha diferentes. Para alterar esse comportamento e atribuir o mesmo número de linha para o mesmo resultado em um grupo, use RANK()
ou DENSE_RANK()
em vez de ROW_NUMBER()
. Na consulta externa, você seleciona todos os dados do CTE (
added_row_number
) e use um WHERE
condição para especificar qual linha exibir de cada grupo. Aqui, queremos exibir a primeira linha, então a condição é row_number = 1
. Observe que você pode modificar facilmente a solução para obter, por exemplo, a segunda linha de cada grupo.
WITH added_row_number AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number FROM exam_results ) SELECT * FROM added_row_number WHERE row_number = 2;
Aqui está o resultado:
first_name | last_name | ano | resultado | número_linha |
---|---|---|---|---|
Kate | Smith | 2019 | 41 | 2 |
Editar | Preto | 2020 | 43 | 2 |
Por outro lado, se você deseja obter as linhas com o segundo valor mais alto de
result
dentro de cada grupo, você deve usar o DENSE_RANK()
função. Enquanto o ROW_NUMBER()
A função cria números consecutivos para cada linha em um grupo, resultando em valores diferentes atribuídos às linhas com o mesmo resultado, o DENSE_RANK()
função dá o mesmo número para as linhas com o mesmo resultado. WITH added_dense_rank AS ( SELECT *, DENSE_RANK() OVER(PARTITION BY year ORDER BY result DESC) AS rank FROM exam_results ) SELECT * FROM added_dense_rank WHERE rank = 2;
first_name | last_name | ano | resultado | classificação |
---|---|---|---|---|
Kate | Smith | 2019 | 41 | 2 |
João | Klein | 2020 | 40 | 2 |
Você pode ver que John Klein tem o segundo maior valor de
result (40)
para o ano de 2020. John Klein é, na verdade, a terceira pessoa do grupo, mas os dois primeiros alunos têm o mesmo result
e ambos têm rank = 1
.