SQL: SELECT, INSERT, UPDATE e DELETE no PostgreSQL
Aprenda as quatro operações básicas de SQL numa tabela de livros, com WHERE, RETURNING, constraints, saídas reais e um roteiro contra alterações acidentais.
SELECT, INSERT, UPDATE e DELETE são os quatro comandos usados para ler,
criar, alterar e remover linhas em SQL. Nesta lição, você vai aplicar os quatro
no catálogo de uma livraria e conferir cada mudança pela saída do PostgreSQL.
O conjunto costuma receber o nome CRUD: create, read, update e delete.
INSERT faz o create, SELECT faz o read e os outros dois mantêm o próprio
nome. CRUD é uma sigla de aplicação; no banco, os nomes que aparecem na
documentação e nos logs são os comandos SQL.
Pense no balcão de estoque. INSERT abre uma ficha, SELECT consulta fichas,
UPDATE corrige uma ficha existente e DELETE a retira do catálogo. A tabela é
o fichário e WHERE é o critério escrito no pedido do atendente. O limite da
analogia: SQL trabalha com conjuntos. Um comando pode atingir zero, uma ou
milhares de linhas de uma vez, e o banco não pergunta se era isso mesmo que você
queria.
Confira onde você está antes de escrever
Abra o psql conectado ao database livraria. Antes de criar qualquer coisa,
confirme database, usuário e versão. Esse hábito evita executar uma manutenção
na base errada:
SELECT current_database() AS banco,
current_user AS usuario,
current_setting('server_version') AS versao;Em produção, o usuário dificilmente será postgres; ele deve ter somente as
permissões necessárias. Aqui usamos o administrador porque o banco é um
laboratório local isolado.
A tabela já nasce sabendo o que rejeitar
O catálogo precisa de identidade, título, autor, preço, estoque e um marcador de ativo. A estrutura abaixo também coloca as duas regras numéricas no banco: Use um database vazio: este laboratório é independente dos outros artigos.
CREATE TABLE livros (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
titulo text NOT NULL,
autor text NOT NULL,
preco numeric(10,2) NOT NULL CHECK (preco > 0),
estoque integer NOT NULL DEFAULT 0 CHECK (estoque >= 0),
ativo boolean NOT NULL DEFAULT true
);numeric(10,2) guarda um valor decimal com duas casas sem a aproximação binária
de ponto flutuante. DEFAULT não significa “aceita qualquer coisa”: ele entra
somente quando a coluna é omitida. Se alguém enviar NULL para estoque, o
NOT NULL ainda rejeita.
Se chave, tipo e constraint ainda parecem decisões soltas, o guia de PostgreSQL mostra o lugar de cada uma no banco.
INSERT: cadastre e peça o recibo da linha
Liste as colunas no comando. Assim, os valores continuam ligados a nomes mesmo
que a tabela ganhe outro campo no futuro. RETURNING traz os ids gerados e os
valores que realmente entraram:
INSERT INTO livros (titulo, autor, preco, estoque)
VALUES
('Dom Casmurro', 'Machado de Assis', 39.90, 8),
('Torto Arado', 'Itamar Vieira Junior', 54.90, 3),
('A Hora da Estrela', 'Clarice Lispector', 34.50, 0)
RETURNING id, titulo, preco, estoque;INSERT 0 3
INSERT 0 3 é a tag do comando: três linhas foram inseridas. A tabela acima
dela veio do RETURNING. Numa API, você pode devolver esse id ao cliente sem
fazer outra viagem ao banco.
SELECT: descreva o conjunto que quer receber
Agora a pergunta é “quais livros ativos estão disponíveis, do mais barato para o mais caro, no máximo dois?”. Cada parte da consulta corresponde a um pedaço dessa frase:
SELECT titulo, preco, estoque
FROM livros
WHERE ativo = true AND estoque > 0
ORDER BY preco
LIMIT 2;WHERE não é um laço escrito por você; é uma condição que cada linha do
resultado precisa satisfazer. A ordem sem ASC é crescente. LIMIT corta a
saída depois da ordenação — sem ORDER BY, falar em “primeiros dois” não tem um
significado estável.
Um SELECT também pode calcular colunas que não estão armazenadas. O preço com
10% de desconto nasce no resultado, sem alterar o catálogo:
SELECT titulo,
preco,
round(preco * 0.90, 2) AS preco_com_desconto
FROM livros
ORDER BY id;AS cria um apelido para a coluna do resultado. Ele não renomeia a coluna da
tabela. Essa diferença fica importante quando o SELECT alimentar uma API ou
um relatório.
UPDATE: mude só o conjunto que você conferiu
A Livraria Horizonte recebeu cinco exemplares de Torto Arado e ajustou o
preço. A expressão usa o estoque atual da própria linha:
UPDATE livros
SET estoque = estoque + 5,
preco = 52.90
WHERE titulo = 'Torto Arado'
RETURNING id, titulo, preco, estoque;UPDATE 1
O UPDATE 1 merece tanta atenção quanto os valores. Se você esperava uma linha
e recebeu UPDATE 0, o filtro não encontrou nada. Se recebeu UPDATE 300, pare
e investigue antes de seguir. A contagem não é decoração do terminal; é uma
checagem da intenção.
Antes de qualquer UPDATE manual, troque temporariamente a primeira palavra por
SELECT * e confira as linhas com o mesmo WHERE. Depois restaure o comando.
Quando a alteração envolver mais de uma tabela, use a
lição de transações.
O erro de constraint protege o estado anterior
Vamos tentar deixar o estoque negativo. A sintaxe está correta, mas o valor viola a regra declarada na criação da tabela:
UPDATE livros
SET estoque = -1
WHERE id = 1;O PostgreSQL avalia a nova versão da linha, vê que estoque >= 0 seria falso e
rejeita o comando inteiro. O dado anterior não fica parcialmente alterado:
SELECT id, titulo, estoque
FROM livros
WHERE id = 1;Esse erro não pede que você remova a constraint. Ele avisa que a aplicação
tentou produzir um estado proibido. A correção pode ser validar quantidade,
recusar a venda ou usar um UPDATE condicionado por estoque disponível.
DELETE: remova linhas, não a estrutura
Para tornar o efeito visível, cadastre uma linha desativada e depois remova somente registros nessa condição:
INSERT INTO livros (titulo, autor, preco, estoque, ativo)
VALUES ('Cadastro duplicado', 'Equipe da loja', 10.00, 0, false)
RETURNING id, titulo, ativo;INSERT 0 1
DELETE FROM livros
WHERE ativo = false
RETURNING id, titulo;DELETE 1
DELETE remove linhas; DROP TABLE remove a tabela e sua definição. Sem
WHERE, DELETE FROM livros apaga todas as linhas e o PostgreSQL não abre uma
caixa de confirmação. O roteiro seguro é o mesmo do UPDATE: SELECT com o
filtro, confira a contagem, execute dentro de transação quando houver risco e
leia a tag final.
Feche a rodada com uma pergunta de negócio
Uma consulta agregada confirma o estado final: três títulos permanecem e há 16 unidades em estoque.
SELECT count(*) AS livros_restantes,
sum(estoque) AS unidades_em_estoque
FROM livros;Agora faça sua missão: cadastre Quarto de Despejo com cinco unidades, consulte
somente livros abaixo de R$ 50, venda duas unidades com UPDATE ... RETURNING
e desative um cadastro sem excluí-lo. O resultado passa se a última consulta
mostrar quatro livros, o estoque novo for três e nenhuma linha fora do seu
WHERE tiver mudado.
Uma tabela só foi suficiente para aprender os verbos. A livraria real separa autor, cliente, pedido e item para não repetir a mesma informação. O próximo passo é combinar essas tabelas com JOIN; o desenho que cria essas relações aparece depois em modelagem relacional.
Perguntas frequentes
Qual a diferença entre SELECT e os outros três comandos?
UPDATE e DELETE precisam de WHERE?
Para que serve RETURNING no PostgreSQL?
Por que não usar SELECT asterisco em toda consulta?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em PostgreSQL 18.6 em postgres:18-alpine e Docker 29.5.3, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- PostgreSQL 18 — Consultando uma tabela — postgresql.org
- PostgreSQL 18 — Inserindo linhas — postgresql.org
- PostgreSQL 18 — UPDATE — postgresql.org
- PostgreSQL 18 — Dados modificados com RETURNING — postgresql.org


