Banco de Dados I — 2026.2

Aula 5
Introdução ao SQL & DDL

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

Agenda de hoje

O que é SQL?

Structured Query Language — linguagem padrão para definir, manipular e consultar dados em bancos relacionais. Criada na IBM nos anos 70 (System R) e padronizada pela ANSI (1986) e ISO (1987).

Subconjuntos da SQL

DDL

Data Definition Language

CREATE, ALTER, DROP — define a estrutura

DML

Data Manipulation Language

INSERT, UPDATE, DELETE — altera os dados

DQL

Data Query Language

SELECT — consulta os dados

DCL

Data Control Language

GRANT, REVOKE — permissões

Hoje focamos em DDL — em particular, CREATE TABLE.

Por que essa separação importa?

Não é rótulo didático — equipes separam por risco, permissão e velocidade de mudança.

🔐 Permissões (DCL)

App em produção só tem DML/DQL. DDL fica com usuário separado, usado em migrations.

Ex.: SQL Server tem o role db_ddladmin — apps não devem entrar nele.

🚀 Ciclos de vida

DDL muda raramente, via migrations versionadas. DML/DQL roda milhões de vezes ao dia.

Alterar schema dentro de um endpoint = receita de outage.

⚠️ Transações

Em Oracle/MySQL, DDL faz commit implícito: DROP TABLE não tem ROLLBACK.

PostgreSQL é exceção — DDL é transacional.

📋 Auditoria

DDL vai para migrations no Git; DQL no código do app; DCL na security matrix.

Cada categoria tem fluxo de aprovação próprio.

CREATE TABLE — Sintaxe

CREATE TABLE nome_tabela (
    coluna1  TIPO  [restrições_de_coluna],
    coluna2  TIPO  [restrições_de_coluna],
    ...,
    [restrições_de_tabela]
);

Primeiro exemplo

CREATE TABLE aluno (
    matricula  SERIAL       PRIMARY KEY,
    nome       VARCHAR(100) NOT NULL,
    email      VARCHAR(150) UNIQUE,
    dt_nasc    DATE,
    ativo      BOOLEAN      DEFAULT TRUE
);

SERIAL

Inteiro com sequência automática (1, 2, 3…). Comum em PKs sintéticas.

PRIMARY KEY

Implica NOT NULL + UNIQUE automaticamente.

DEFAULT

Valor assumido quando o INSERT omite a coluna.

Tipos de Dados — PostgreSQL

Numéricos

INTEGER — inteiro de 4 bytes
BIGINT — inteiro de 8 bytes
SERIAL — inteiro com auto-incremento
NUMERIC(p,s) — decimal exato (p dígitos, s decimais)
REAL / DOUBLE PRECISION — ponto flutuante

Texto

VARCHAR(n) — texto com tamanho máximo
CHAR(n) — texto de tamanho fixo
TEXT — texto sem limite

Data e Hora

DATE — data (YYYY-MM-DD)
TIME — hora
TIMESTAMP — data + hora
INTERVAL — intervalo de tempo

Outros

BOOLEANTRUE / FALSE / NULL
UUID — identificador universal único
JSON / JSONB — dados estruturados

Constraints (Restrições)

Constraints são regras que o SGBD garante automaticamente. Tudo que viola uma constraint é rejeitado — os dados nunca chegam ao estado inválido.

ConstraintO que garanteOnde costuma aparecer
PRIMARY KEYIdentificador único, não-nuloToda tabela tem uma
FOREIGN KEYIntegridade referencial entre tabelasLados N de relacionamentos / tabelas associativas
NOT NULLColuna obrigatória (sempre preenchida)Atributos essenciais (nome, e-mail…)
UNIQUEValores não se repetem na colunaCPF, e-mail, slug, código natural
CHECKExpressão booleana sobre o valorFaixas, formatos, regras de negócio

PRIMARY KEY

-- PK simples (na coluna)
CREATE TABLE produto (
    codigo     SERIAL       PRIMARY KEY,
    descricao  VARCHAR(200) NOT NULL
);

-- PK composta (restrição de tabela)
CREATE TABLE item_pedido (
    pedido_id   INTEGER,
    produto_id  INTEGER,
    qtd         INTEGER NOT NULL,
    PRIMARY KEY (pedido_id, produto_id)
);

FOREIGN KEY

CREATE TABLE pedido (
    id         SERIAL   PRIMARY KEY,
    data       DATE     NOT NULL DEFAULT CURRENT_DATE,
    cliente_id INTEGER  NOT NULL
               REFERENCES cliente(id)
               ON DELETE RESTRICT
               ON UPDATE CASCADE
);

RESTRICT / NO ACTION

Bloqueia a operação se houver dependentes. Default seguro.

CASCADE

Propaga a operação para os dependentes (deleta/atualiza junto).

SET NULL / SET DEFAULT

Limpa a FK do dependente, mantendo a linha.

NOT NULL & UNIQUE

NOT NULL

A coluna nunca pode ficar vazia. Use para atributos que são parte da identidade da entidade.

nome VARCHAR(100) NOT NULL

UNIQUE

Não permite valores repetidos. Permite vários NULLs (NULL ≠ NULL no PostgreSQL).

email VARCHAR(150) UNIQUE

💡 UNIQUE sozinho não impede NULL. Quer e-mail único e obrigatório? email VARCHAR(150) UNIQUE NOT NULL.

UNIQUE também aceita múltiplas colunas: UNIQUE (col_a, col_b) — a combinação é única.

CHECK

Valida o valor da coluna (ou linha) com uma expressão booleana. Se a expressão é falsa, o INSERT/UPDATE é rejeitado.

CREATE TABLE funcionario (
    id        SERIAL       PRIMARY KEY,
    nome      VARCHAR(100) NOT NULL,
    salario   NUMERIC(10,2) CHECK (salario > 0),
    cargo     VARCHAR(20)
              CHECK (cargo IN ('jr', 'pl', 'sr')),
    admissao  DATE,
    demissao  DATE,
    CHECK (demissao IS NULL OR demissao >= admissao)
);

CHECK de linha (último) consegue comparar duas colunas — restrição de tabela.

Boas práticas ao escrever SQL

Liste as colunas — evite SELECT *

Prefira SELECT nome, email FROM cliente em vez de SELECT *. Você documenta o que precisa, evita trazer colunas pesadas e quebra menos quando o esquema muda.

SELECT * em produção é code smell — útil só para exploração rápida.

Ponha as regras no banco

Constraints (NOT NULL, CHECK, FK) valem para todos os clientes — app, scripts, console. Não confie só na validação da aplicação.

Ordem importa

Crie primeiro as tabelas referenciadas e só depois as referenciadoras. Caso contrário, a FK falha logo no CREATE.

Exemplo completo — E-commerce (1/2)

CREATE TABLE cliente (
    id      SERIAL       PRIMARY KEY,
    cpf     CHAR(11)     UNIQUE NOT NULL
                          CHECK (char_length(cpf) = 11),
    nome    VARCHAR(100) NOT NULL,
    email   VARCHAR(150) UNIQUE NOT NULL
);

CREATE TABLE produto (
    id      SERIAL        PRIMARY KEY,
    nome    VARCHAR(200) NOT NULL,
    preco   NUMERIC(10,2) NOT NULL CHECK (preco > 0),
    estoque INTEGER      NOT NULL DEFAULT 0
                          CHECK (estoque >= 0)
);

Entidades fortes — cliente e produto não dependem de ninguém. Vêm primeiro.

Exemplo completo — E-commerce (2/2)

CREATE TABLE pedido (
    id         SERIAL  PRIMARY KEY,
    data       DATE    NOT NULL DEFAULT CURRENT_DATE,
    cliente_id INTEGER NOT NULL REFERENCES cliente(id)
);

CREATE TABLE item_pedido (
    pedido_id  INTEGER REFERENCES pedido(id) ON DELETE CASCADE,
    produto_id INTEGER REFERENCES produto(id),
    qtd        INTEGER NOT NULL CHECK (qtd > 0),
    PRIMARY KEY (pedido_id, produto_id)
);

pedido tem FK para cliente. item_pedido é a associativa N:M com PK composta — apaga em cascata se o pedido sumir.

Exercício 1 — Fácil

Catálogo de Livros

Uma pequena biblioteca quer cadastrar seus livros. Cada livro tem um ISBN de 13 caracteres (identificador único e obrigatório), um título (até 200 caracteres, obrigatório), um autor (até 100 caracteres, obrigatório), um ano de publicação (entre 1900 e 2026) e um preço em reais (precisa ser maior que zero).

Você precisa

Escrever um único CREATE TABLE livro (...) com PK, tipos adequados, NOT NULL onde necessário e CHECK para ano e preço.

Critério de aceite

O DB Fiddle deve executar sem erro. Tente um INSERT com ano = 1800 e confirme que ele falha.

▶ Abrir DB Fiddle (PostgreSQL)

Exercício 2 — Médio

Clínica Veterinária

Uma clínica precisa cadastrar tutores (CPF de 11 caracteres único, nome obrigatório, telefone, e-mail único) e seus animais (id auto-gerado, nome obrigatório, espécie restrita a cão, gato, ave, outro, data de nascimento e o tutor responsável). Um animal sempre pertence a um tutor; se o tutor for removido, o cadastro do animal deve ser bloqueado.

Você precisa

Criar duas tabelas (tutor e animal), com a FK em animal apontando para tutor. Aplique UNIQUE no CPF e e-mail, CHECK na espécie e ON DELETE RESTRICT na FK.

Critério de aceite

Insira um tutor e dois animais. Tente DELETE FROM tutor nesse tutor — deve falhar. Tente cadastrar espécie "peixe" — deve falhar.

▶ Abrir DB Fiddle (PostgreSQL)

Exercício 3 — Difícil

Sistema de Reservas de Hotel

Modele um pequeno sistema com três tabelas: hospede (id, nome, e-mail único), quarto (numero como PK, tipo restrito a standard, luxo, suite, diaria > 0) e reserva (um hóspede reserva um quarto entre uma data de check-in e uma data de check-out). A PK da reserva é a combinação (quarto, data_checkin). Regras: check-out posterior ao check-in; se um hóspede for removido, suas reservas devem ser apagadas em cascata; se um quarto for removido, suas reservas não podem ser apagadas (bloqueia).

Você precisa

Definir três CREATE TABLE com PK simples e composta, CHECK de coluna e de linha (datas), e duas FKs com ON DELETE diferentes (CASCADE e RESTRICT).

Critério de aceite

Insira hóspede, quarto e reserva. Tente reserva com check-out anterior ao check-in — deve falhar. Tente duas reservas no mesmo (quarto, data_checkin) — deve falhar pela PK composta.

▶ Abrir DB Fiddle (PostgreSQL)

Banco de Dados I — 2026.2

Próxima aula: DDL — parte 2

📖 Heuser, cap. 7  |  Silberschatz, cap. 4  |  Apostila Alura — SQL