CREATE TABLE: sintaxe e estruturaStructured 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).
PALAVRAS-CHAVE em maiúsculoData Definition Language
CREATE, ALTER, DROP — define a estrutura
Data Manipulation Language
INSERT, UPDATE, DELETE — altera os dados
Data Query Language
SELECT — consulta os dados
Data Control Language
GRANT, REVOKE — permissões
Hoje focamos em DDL — em particular, CREATE TABLE.
Não é rótulo didático — equipes separam por risco, permissão e velocidade de mudança.
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.
DDL muda raramente, via migrations versionadas. DML/DQL roda milhões de vezes ao dia.
Alterar schema dentro de um endpoint = receita de outage.
Em Oracle/MySQL, DDL faz commit implícito: DROP TABLE não tem ROLLBACK.
PostgreSQL é exceção — DDL é transacional.
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 — SintaxeCREATE TABLE nome_tabela ( coluna1 TIPO [restrições_de_coluna], coluna2 TIPO [restrições_de_coluna], ..., [restrições_de_tabela] );
NOT NULL, UNIQUE)CREATE TABLE aluno ( matricula SERIAL PRIMARY KEY, nome VARCHAR(100) NOT NULL, email VARCHAR(150) UNIQUE, dt_nasc DATE, ativo BOOLEAN DEFAULT TRUE );
SERIALInteiro com sequência automática (1, 2, 3…). Comum em PKs sintéticas.
PRIMARY KEYImplica NOT NULL + UNIQUE automaticamente.
DEFAULTValor assumido quando o INSERT omite a coluna.
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
VARCHAR(n) — texto com tamanho máximo
CHAR(n) — texto de tamanho fixo
TEXT — texto sem limite
DATE — data (YYYY-MM-DD)
TIME — hora
TIMESTAMP — data + hora
INTERVAL — intervalo de tempo
BOOLEAN — TRUE / FALSE / NULL
UUID — identificador universal único
JSON / JSONB — dados estruturados
Constraints são regras que o SGBD garante automaticamente. Tudo que viola uma constraint é rejeitado — os dados nunca chegam ao estado inválido.
| Constraint | O que garante | Onde costuma aparecer |
|---|---|---|
| PRIMARY KEY | Identificador único, não-nulo | Toda tabela tem uma |
| FOREIGN KEY | Integridade referencial entre tabelas | Lados N de relacionamentos / tabelas associativas |
| NOT NULL | Coluna obrigatória (sempre preenchida) | Atributos essenciais (nome, e-mail…) |
| UNIQUE | Valores não se repetem na coluna | CPF, e-mail, slug, código natural |
| CHECK | Expressão booleana sobre o valor | Faixas, 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) );
NOT NULL e UNIQUEFOREIGN KEYCREATE 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 ACTIONBloqueia a operação se houver dependentes. Default seguro.
CASCADEPropaga a operação para os dependentes (deleta/atualiza junto).
SET NULL / SET DEFAULTLimpa a FK do dependente, mantendo a linha.
NOT NULL & UNIQUENOT NULLA coluna nunca pode ficar vazia. Use para atributos que são parte da identidade da entidade.
nome VARCHAR(100) NOT NULL
UNIQUENã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.
CHECKValida 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.
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.
Constraints (NOT NULL, CHECK, FK) valem para todos os clientes — app, scripts, console. Não confie só na validação da aplicação.
Crie primeiro as tabelas referenciadas e só depois as referenciadoras. Caso contrário, a FK falha logo no CREATE.
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.
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.
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).
Escrever um único CREATE TABLE livro (...) com PK, tipos adequados, NOT NULL onde necessário e CHECK para ano e preço.
O DB Fiddle deve executar sem erro. Tente um INSERT com ano = 1800 e confirme que ele falha.
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.
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.
Insira um tutor e dois animais. Tente DELETE FROM tutor nesse tutor — deve falhar. Tente cadastrar espécie "peixe" — deve falhar.
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).
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).
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.
ALTER TABLE, DROP TABLE, laboratório📖 Heuser, cap. 7 | Silberschatz, cap. 4 | Apostila Alura — SQL