Perguntas de entrevista de SQL, com respostas que você pode executar

A maioria das entrevistas de SQL repete as mesmas dez ou doze ideias. A seguir estão as mais comuns, com uma resposta curta e uma consulta que funciona no PostgreSQL. Os exemplos usam um pequeno banco de dados de padaria (customers, products, orders e order_items), para que você possa testar cada um em uma lição deste site.

Encontre o segundo maior valor

Uma resposta simples é ordenar em ordem decrescente e pular a primeira linha. Ela retorna uma única linha, mesmo que várias linhas tenham o segundo preço.

Se os empates importarem, calcule a classificação com DENSE_RANK e fique com a classificação 2. DENSE_RANK não deixa lacunas depois dos empates, então a classificação 2 é sempre o segundo valor distinto.

SELECT name, unit_price
FROM (
  SELECT name, unit_price, DENSE_RANK() OVER (ORDER BY unit_price DESC) AS r
  FROM products
) AS ranked
WHERE r = 2;

Resultado: A fatia de Chocolate cake, por 4.20.

Encontre duplicatas

Agrupe pela coluna que deveria ser única e mantenha os grupos que têm mais de uma linha.

SELECT order_date, COUNT(*) AS orders
FROM orders
GROUP BY order_date
HAVING COUNT(*) > 1
ORDER BY order_date;

Resultado: Três datas, cada uma com dois pedidos: 1, 2 e 6 de setembro.

Encontre linhas sem correspondência

Use um LEFT JOIN e mantenha as linhas em que o lado direito é NULL, ou use NOT EXISTS. As duas respostas dizem “quais clientes nunca fizeram pedido?”.

Evite NOT IN com uma subconsulta que pode retornar NULL: um único NULL torna toda a condição desconhecida e você não recebe nenhuma linha.

SELECT c.first_name, c.last_name
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL
ORDER BY c.customer_id;

Resultado: Grace Kim e Hugo Meyer.

Linha principal de cada grupo

Classifique dentro de cada grupo com PARTITION BY e depois mantenha a classificação 1 em uma consulta externa. Use RANK para manter os empates, ou ROW_NUMBER para manter exatamente uma linha por grupo.

SELECT category, name, units
FROM (
  SELECT p.category, p.name, SUM(oi.quantity) AS units,
         RANK() OVER (PARTITION BY p.category ORDER BY SUM(oi.quantity) DESC) AS rk
  FROM products AS p
  JOIN order_items AS oi ON oi.product_id = p.product_id
  GROUP BY p.category, p.name
) AS ranked
WHERE rk = 1
ORDER BY category, name;

Resultado: O item mais vendido de cada categoria. Cake aparece duas vezes porque empata em 3 unidades.

Total acumulado

Use SUM como função de janela com ORDER BY. Ordene por uma coluna sem empates, senão as linhas que empatam são somadas juntas.

SELECT order_id,
       SUM(quantity) AS units,
       SUM(SUM(quantity)) OVER (ORDER BY order_id) AS running_units
FROM order_items
GROUP BY order_id
ORDER BY order_id;

Resultado: Unidades por pedido e o total até ali, terminando em 56.

WHERE versus HAVING

WHERE filtra as linhas antes de agrupá-las, por isso não pode usar uma função de agregação. HAVING filtra os grupos depois da agregação. No exemplo, WHERE remove primeiro os produtos baratos, e HAVING mantém as categorias com pelo menos dois dos produtos restantes.

SELECT category, COUNT(*) AS products
FROM products
WHERE unit_price > 2.50
GROUP BY category
HAVING COUNT(*) >= 2
ORDER BY category;

Resultado: Bread e cake, com dois produtos cada acima de 2.50. Pastry e drinks têm só um produto acima de 2.50 e ficam de fora.

UNION versus UNION ALL

UNION remove linhas duplicadas, o que exige trabalho extra. UNION ALL mantém tudo e é mais rápido. Use UNION ALL, a não ser que você precise remover as duplicatas.

SELECT city FROM customers
UNION
SELECT city FROM customers
ORDER BY city;

Resultado: Cinco cidades, cada uma uma vez. Com UNION ALL haveria 16 linhas.

Perguntas comuns

Qual é a diferença entre DELETE, TRUNCATE e DROP?
DELETE remove as linhas que você escolher (WHERE) e pode ser desfeito com rollback. TRUNCATE esvazia a tabela inteira rapidamente e mantém a estrutura. DROP remove a própria tabela, com sua estrutura e seus dados.
Qual é a diferença entre INNER JOIN e LEFT JOIN?
INNER JOIN mantém apenas as linhas que têm correspondência nos dois lados. LEFT JOIN mantém todas as linhas da tabela da esquerda e preenche o lado direito com NULL onde não há correspondência.
Qual é a diferença entre RANK, DENSE_RANK e ROW_NUMBER?
Os três numeram as linhas de uma ordem. RANK dá o mesmo número aos empates e pula os números seguintes. DENSE_RANK dá o mesmo número aos empates e não pula. ROW_NUMBER nunca repete um número.
Qual dialeto de SQL essas respostas usam?
PostgreSQL. LIMIT/OFFSET, funções de janela, CTEs e junções funcionam quase em todos. No SQL Server use TOP ou OFFSET FETCH no lugar de LIMIT, e no Oracle use FETCH FIRST.

Pratique