Banco de Dados I — 2026.2

Aula 9
SELECT: WHERE, ORDER BY & LIMIT

Consultando o banco do Alumnus que criamos na aula passada

👨‍🏫 Prof. Gustavo Pinto 🏛️ UFPA 📅 21 de maio de 2026 🕣 7h30 – 9h10

Agenda de hoje

De onde viemos

Aulas 5–7

DDL (CREATE/ALTER/DROP) e DML (INSERT/UPDATE/DELETE) — estrutura e dados.

Aula 8 — Alumnus

Modelamos e populamos o banco do Alumnus: grupos, pesquisadores, relacionamentos, notas, lembretes e deadlines.

Hoje a pergunta muda: como extrair informação útil do banco?

Por que SELECT é o comando que mais importa

Um banco só vale a pena se você consegue perguntar coisas a ele — e SELECT é a forma como se faz perguntas em SQL.

Anatomia do SELECT

SELECT   <colunas>      -- o que mostrar (projeção)
FROM     <tabela>       -- de onde tirar
WHERE    <condição>     -- quais linhas (filtro)
ORDER BY <colunas>      -- em que ordem
LIMIT    <n>;           -- quantas linhas trazer

SQL é declarativo: você descreve o que quer, não como buscar. O SGBD decide o plano de execução.

Projeção, alias e DISTINCT

-- Todas as colunas (use com parcimônia)
SELECT * FROM pesquisador;

-- Só o que interessa
SELECT nome, status FROM pesquisador;

-- Alias: renomeia a coluna no resultado
SELECT nome AS "Pesquisador",
       status AS "Nível"
FROM pesquisador;

-- DISTINCT: elimina duplicatas no resultado
SELECT DISTINCT status FROM pesquisador;
-- → graduacao, mestrado, doutorado, postdoc

WHERE — filtrando linhas

-- Igualdade e comparação
SELECT nome FROM pesquisador WHERE status = 'doutorado';
SELECT *    FROM lembrete    WHERE data_vencimento < CURRENT_DATE;

-- BETWEEN: intervalo inclusivo nas duas pontas
SELECT * FROM nota
WHERE data BETWEEN '2026-05-01' AND '2026-05-31';

-- IN: pertence a uma lista
SELECT nome FROM pesquisador
WHERE status IN ('mestrado', 'doutorado');

-- LIKE: padrão de texto   (% = qualquer trecho   _ = um caractere)
SELECT nome FROM pesquisador WHERE email LIKE '%@ufpa.br';

-- IS NULL / IS NOT NULL  — nunca use "= NULL"
SELECT * FROM lembrete WHERE concluido_em IS NULL;

O cuidado com NULL

NULL não é "vazio" nem "zero" — é desconhecido. Qualquer comparação com NULLNULL, que não é verdadeiro.

-- ❌ não retorna nada, mesmo que existam nulos
SELECT * FROM lembrete WHERE concluido_em = NULL;

-- ✅ a forma correta
SELECT * FROM lembrete WHERE concluido_em IS NULL;

-- "diferente" também precisa cuidar do NULL
SELECT * FROM pesquisador
WHERE orientador_id <> 1 OR orientador_id IS NULL;

Operadores lógicos — AND, OR, NOT

-- AND: as duas condições precisam ser verdadeiras
SELECT nome FROM pesquisador
WHERE status = 'doutorado' AND grupo_id = 1;

-- OR: pelo menos uma é verdadeira
SELECT nome FROM pesquisador
WHERE status = 'mestrado' OR status = 'doutorado';

-- NOT: nega a condição
SELECT * FROM lembrete WHERE NOT concluido_em IS NULL;

-- Misturando — parênteses evitam ambiguidade
SELECT nome FROM pesquisador
WHERE (status = 'mestrado' OR status = 'doutorado')
  AND email LIKE '%@ufpa.br';

AND tem precedência sobre OR — quando em dúvida, parentize.

ORDER BY — ordenando o resultado

-- Crescente é o padrão (ASC)
SELECT nome, status FROM pesquisador ORDER BY nome;

-- Decrescente
SELECT texto, data FROM nota ORDER BY data DESC;

-- Múltiplas colunas: primeiro grupo, depois nome dentro do grupo
SELECT grupo_id, nome FROM pesquisador
ORDER BY grupo_id ASC, nome ASC;

-- Por uma expressão / alias
SELECT nome, LENGTH(nome) AS tam
FROM pesquisador ORDER BY tam DESC;

Sem ORDER BY, a ordem das linhas não é garantida — mesmo que pareça consistente.

LIMIT e OFFSET — só as N linhas que importam

-- As 5 notas mais recentes
SELECT texto, data FROM nota
ORDER BY data DESC LIMIT 5;

-- A próxima deadline a vencer
SELECT nome, data_limite FROM deadline
WHERE data_limite >= CURRENT_DATE
ORDER BY data_limite ASC LIMIT 1;

-- Paginação: pula 10 e pega as próximas 5
SELECT nome FROM pesquisador
ORDER BY nome LIMIT 5 OFFSET 10;

LIMIT sem ORDER BY traz "5 quaisquer" — pode mudar entre execuções. Sempre ordene antes de limitar.

Mão na massa

Laboratório
consultas no Alumnus

Suba o banco da aula passada no DB Fiddle (PostgreSQL) e resolva as consultas a seguir.

Roteiro do laboratório

① Reabrir

Suba o banco do Alumnus da aula passada no DB Fiddle (PostgreSQL).

~5 min

② Consultar

Resolva as 8 consultas do próximo slide, do mais simples ao mais composto.

~50 min, em dupla

③ Compartilhar

Cada dupla apresenta uma consulta e explica o porquê de cada cláusula.

~25 min

8 consultas no Alumnus

Básicas (WHERE + ORDER BY)

1. Todos os pesquisadores de doutorado, ordenados por nome.
2. Lembretes ainda pendentes (sem data de conclusão).
3. Notas escritas em maio de 2026, da mais recente para a mais antiga.
4. Pesquisadores cujo e-mail não é da UFPA.

Intermediárias (LIKE, IN, BETWEEN, LIMIT)

5. Pesquisadores de mestrado ou doutorado cujo nome começa com "A".
6. As 3 deadlines mais próximas ainda por vencer.
7. Lembretes que vencem na semana atual (use BETWEEN).
8. Para cada pesquisador, mostre nome e o domínio do e-mail.

Desafio: a nota mais recente escrita sobre a pesquisadora "Ana Souza" — uma única linha como resposta.

Bônus

Para saber mais
funções escalares & pegadinhas

Funções escalares — o que são

Recebem um valor por linha e devolvem um valor por linha. Diferente de agregações (próxima aula), que colapsam várias linhas em uma.

Texto

UPPER, LOWER, LENGTH, TRIM, CONCAT, SUBSTRING

Número

ABS, ROUND, CEIL, FLOOR, MOD

Data e nulo

CURRENT_DATE, NOW(), EXTRACT, COALESCE, CAST

Funções de texto

-- Padronizar caixa
SELECT UPPER(nome), LOWER(email) FROM pesquisador;

-- Tamanho do texto
SELECT nome, LENGTH(nome) AS tam FROM pesquisador;

-- Tirar espaços nas pontas (útil em dados sujos)
SELECT TRIM(nome) FROM pesquisador;

-- Concatenar (PostgreSQL usa ||)
SELECT nome || ' <' || email || '>' AS contato
FROM pesquisador;

-- Pegar um pedaço do texto
SELECT SUBSTRING(email FROM '@(.*)$') AS dominio
FROM pesquisador;

Funções de número, data e nulos

-- Número
SELECT ROUND(3.567, 2),  CEIL(3.2),  FLOOR(3.8);

-- Hoje e agora
SELECT CURRENT_DATE, NOW();

-- Dias até a deadline
SELECT nome, data_limite - CURRENT_DATE AS dias_restantes
FROM deadline
WHERE data_limite >= CURRENT_DATE;

-- Extrair parte de uma data
SELECT EXTRACT(YEAR FROM data) AS ano,
       EXTRACT(MONTH FROM data) AS mes
FROM nota;

-- COALESCE: primeiro valor não-nulo (default na consulta)
SELECT texto, COALESCE(concluido_em::text, 'pendente') AS situacao
FROM lembrete;

Armadilhas comuns

= NULL em vez de IS NULL

Comparar com NULL sempre dá NULL (não verdadeiro). Resultado: a consulta "funciona" mas vem vazia.

Esquecer ORDER BY com LIMIT

"Os 5 mais recentes" sem ORDER BY data DESC traz 5 linhas quaisquer.

AND e OR sem parênteses

AND tem precedência. A OR B AND C é A OR (B AND C) — quase nunca o que você quis.

SELECT * em tudo

Esconde colunas que você nem sabe que vieram e dificulta ler o resultado. Liste só o que precisa.

Ordem que você escreve ≠ ordem que o SGBD executa

-- Você escreve:
SELECT   nome, UPPER(status)
FROM     pesquisador
WHERE    status IN ('mestrado', 'doutorado')
ORDER BY nome
LIMIT    10;

-- O SGBD executa (conceitualmente):
-- 1. FROM     → lê a tabela
-- 2. WHERE    → filtra linhas
-- 3. SELECT   → projeta colunas
-- 4. ORDER BY → ordena o resultado
-- 5. LIMIT    → corta as N primeiras

Por isso o alias de SELECT funciona em ORDER BY, mas não em WHERE.

Resumo da aula

Próxima Aula — 26/05 (Ter)

SELECT — Agregação
COUNT, SUM, AVG, GROUP BY & HAVING