Mantenha o SQLtest vivo: Ajude a cobrir os custos do servidor de $100 deste mês para manter todas as lições e o playground SQL online. Apoie o projeto →
Código SQL copiado para a área de transferência

Lição 12.4 · Tempo de leitura: ~7 min

"Encontre o segundo maior salário" é uma das perguntas mais frequentes em entrevistas de SQL, normalmente seguida de "agora o N-ésimo" e "agora em cada departamento". Nesta lição, você vai resolvê-la de cinco formas: MAX com uma subconsulta, LIMIT ... OFFSET, DENSE_RANK() e uma subconsulta correlacionada, e vai ver as armadilhas que os entrevistadores procuram: salários repetidos, N-ésimo valor inexistente e grupos.

N-ésimo Maior Salário em SQL

A tarefa parece trivial, e é exatamente por isso que é popular: uma primeira tentativa costuma funcionar nos dados de exemplo e falhar num caso-limite. Uma boa resposta trata três coisas: empates (dois funcionários com o mesmo salário), ausência de resposta (menos de N salários diferentes) e grupos (o N-ésimo salário de cada departamento).

Os exemplos usam a sintaxe do MySQL 8.0 e criam a sua própria tabela, por isso execute-os no playground num banco sem dados de exemplo.

Dados de Exemplo

CREATE TABLE employee (
    id INT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    department VARCHAR(30) NOT NULL,
    salary INT NOT NULL
);

INSERT INTO employee VALUES
    (1, 'Alice', 'Sales', 5000),
    (2, 'Bob', 'Sales', 7000),
    (3, 'Carol', 'Sales', 7000),
    (4, 'Dave', 'IT', 9000),
    (5, 'Eve', 'IT', 6500),
    (6, 'Frank', 'IT', 9000),
    (7, 'Grace', 'HR', 4800),
    (8, 'Heidi', 'HR', 5200);

Os salários distintos, do maior para o menor, são 9000, 7000, 6500, 5200, 5000, 4800. Duas pessoas ganham 9000 e duas ganham 7000, por isso o segundo maior salário é 7000, e não 9000.


Solução 1: o MAX Abaixo do MAX

O segundo maior salário é o maior salário que é menor do que o máximo:

SELECT MAX(salary) AS second_highest_salary
FROM employee
WHERE salary < (SELECT MAX(salary) FROM employee);

Resultado: 7000.

É curto, funciona em qualquer banco de dados, trata empates e devolve NULL quando não há segundo salário, porque o MAX de um conjunto vazio é NULL. O seu limite: só funciona para N = 2. Para o terceiro salário seria preciso mais um nível de aninhamento.


Solução 2: DISTINCT com LIMIT e OFFSET

Ordene os salários do maior para o menor e salte N − 1 deles:

SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 1;

Resultado: 7000.

O DISTINCT é essencial. Sem ele, a lista ordenada é 9000, 9000, 7000, … e o OFFSET 1 cai no segundo 9000:

-- Errado: devolve outra vez o maior salário por causa do empate
SELECT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 1;

Resultado: 9000, o maior salário, e não o segundo maior.

Devolver NULL Quando Não Há Resposta

Se N for maior do que o número de salários distintos, a consulta devolve nenhuma linha. As tarefas de entrevista costumam exigir um NULL nesse caso. Envolva a consulta numa subconsulta escalar: uma subconsulta que não encontra nada devolve NULL.

SELECT (
    SELECT DISTINCT salary
    FROM employee
    ORDER BY salary DESC
    LIMIT 1 OFFSET 9
) AS tenth_highest_salary;

Resultado: uma linha com NULL; há apenas 6 salários distintos.

LIMIT ... OFFSET é sintaxe do MySQL, PostgreSQL e SQLite. O SQL Server e o Oracle 12c+ usam OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY.


Solução 3: DENSE_RANK

Uma função de janela classifica cada linha, e a consulta externa escolhe a posição pretendida. É a solução mais flexível e a que os entrevistadores normalmente esperam:

WITH ranked AS (
    SELECT name, salary,
           DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
    FROM employee
)
SELECT name, salary
FROM ranked
WHERE salary_rank = 2;

Resultado: Bob e Carol, ambos com 7000.

Também responde à pergunta seguinte, "quem ganha o segundo maior salário". Troque 2 por qualquer N. Só o DENSE_RANK está correto aqui:

salaryROW_NUMBERRANKDENSE_RANK
9000111
9000211
7000332
7000432
6500553

O ROW_NUMBER dá números diferentes a salários iguais, e o RANK deixa lacunas depois dos empates, por isso RANK = 2 não encontra ninguém. O DENSE_RANK numera os valores distintos sem lacunas.


Solução 4: Subconsulta Correlacionada

Antes das funções de janela, a resposta clássica era: um salário é o N-ésimo maior se exatamente N salários distintos forem maiores ou iguais a ele.

SELECT DISTINCT e1.salary
FROM employee e1
WHERE 3 = (
    SELECT COUNT(DISTINCT e2.salary)
    FROM employee e2
    WHERE e2.salary >= e1.salary
);

Resultado: 6500, o terceiro maior salário.

Funciona em qualquer banco de dados, mas a subconsulta é executada para cada linha, por isso é lenta numa tabela grande. Mencione-a como alternativa e use DENSE_RANK na prática.


Solução 5: o N-ésimo Maior em Cada Grupo

A pergunta seguinte habitual: "o segundo maior salário de cada departamento". Acrescente PARTITION BY à função de janela:

WITH ranked AS (
    SELECT department, name, salary,
           DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
    FROM employee
)
SELECT department, name, salary
FROM ranked
WHERE salary_rank = 2
ORDER BY department;

Resultado: HR – Grace (4800), IT – Eve (6500), Sales – Alice (5000).

Em Sales, Bob e Carol partilham o maior salário, por isso o segundo maior são os 5000 da Alice. Um departamento com um único salário distinto ficaria fora do resultado.


Que Solução Escolher

SoluçãoNEmpatesSem respostaGrupos
MAX abaixo do MAXSó 2SimNULLDifícil
DISTINCT ... LIMIT OFFSETQualquerCom DISTINCTVazio; NULL numa subconsulta escalarNão
DENSE_RANK()QualquerSimVazioPARTITION BY
Subconsulta correlacionadaQualquerSimVazioAcrescentar condição

Numa entrevista, comece por DENSE_RANK, explique por que não ROW_NUMBER nem RANK, e diga como devolveria NULL.

Prática


Principais conclusões:

  • O segundo maior valor é MAX(x) WHERE x < (SELECT MAX(x) ...); trata empates e devolve NULL quando não existe.
  • Com LIMIT 1 OFFSET N-1, use sempre DISTINCT, ou os empates desviam a resposta.
  • Uma subconsulta escalar transforma "nenhuma linha" em NULL.
  • O DENSE_RANK() resolve qualquer N, devolve os funcionários e, com PARTITION BY, funciona por grupo.
  • ROW_NUMBER e RANK dão respostas erradas quando os valores se repetem.

Perguntas Frequentes

Como encontrar o segundo maior salário em SQL?

A consulta portável mais curta é SELECT MAX(salary) FROM employee WHERE salary < (SELECT MAX(salary) FROM employee);. Devolve o maior salário abaixo do máximo, ignora empates e devolve NULL quando todos ganham o mesmo.

Como encontrar o N-ésimo maior salário em SQL?

Use DENSE_RANK() OVER (ORDER BY salary DESC) numa CTE e filtre pela posição N, ou SELECT DISTINCT salary ... ORDER BY salary DESC LIMIT 1 OFFSET N-1. Ambas tratam corretamente os salários repetidos.

Por que não usar ROW_NUMBER ou RANK?

O ROW_NUMBER numera de forma diferente salários iguais, por isso um empate no topo coloca o mesmo valor em segundo lugar. O RANK salta números depois dos empates: com dois salários máximos, não existe posição 2. O DENSE_RANK numera os valores distintos de forma contínua.

Como devolver NULL se não houver N-ésimo maior salário?

Coloque a consulta dentro de uma subconsulta escalar: SELECT (SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 1 OFFSET 9) AS result;. Quando a consulta interna não encontra nada, a externa devolve na mesma uma linha com NULL.