kb.erickguedes.com
SQL: Domínio de Bancos Relacionais

DML — Modificação de Dados

Aula 6 de 8

INSERT

-- Simples
INSERT INTO clientes (nome, email, telefone) 
VALUES ('João Silva', '[email protected]', '11999999999');

-- Múltiplos registros
INSERT INTO clientes (nome, email) VALUES
    ('Maria', '[email protected]'),
    ('Carlos', '[email protected]'),
    ('Ana', '[email protected]');

-- INSERT com SELECT
INSERT INTO clientes_premium (id, nome, email, total_gasto)
SELECT c.id, c.nome, c.email, SUM(p.total) AS total_gasto
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nome, c.email
HAVING SUM(p.total) > 10000;

-- INSERT ... ON CONFLICT (PostgreSQL)
INSERT INTO clientes (email, nome, telefone)
VALUES ('[email protected]', 'João Atualizado', '11988888888')
ON CONFLICT (email) 
DO UPDATE SET nome = EXCLUDED.nome, telefone = EXCLUDED.telefone;

-- INSERT IGNORE (MySQL)
INSERT IGNORE INTO clientes (email, nome) VALUES ('[email protected]', 'João');

UPDATE

-- CUIDADO: sem WHERE, atualiza TODAS as linhas!
UPDATE clientes SET ativo = true;

-- Com filtro
UPDATE clientes 
SET telefone = '11977777777', 
    data_atualizacao = CURRENT_TIMESTAMP
WHERE id = 10;

-- UPDATE com JOIN (PostgreSQL)
UPDATE pedidos p
SET status = 'cancelado'
FROM clientes c
WHERE p.cliente_id = c.id
  AND c.ativo = false
  AND p.status = 'pendente';

-- UPDATE com subquery
UPDATE produtos
SET preco_venda = preco_venda * 1.1
WHERE categoria_id IN (
    SELECT id FROM categorias WHERE nome = 'Eletrônicos'
);

DELETE

-- CUIDADO: sem WHERE, deleta TUDO!
DELETE FROM pedidos WHERE id = 100;

-- DELETE com JOIN (PostgreSQL)
DELETE FROM pedidos p
USING clientes c
WHERE p.cliente_id = c.id AND c.ativo = false;

-- TRUNCATE (mais rápido que DELETE, não pode ter WHERE)
TRUNCATE TABLE logs_temporarios;

-- Delete em lotes (evitar locks longos)
DELETE FROM pedidos
WHERE data_pedido < '2020-01-01'
LIMIT 1000;  -- PostgreSQL suporta LIMIT em DELETE

MERGE (Upsert)

-- PostgreSQL: INSERT ON CONFLICT
INSERT INTO estoque (produto_id, quantidade)
VALUES (1, 50)
ON CONFLICT (produto_id)
DO UPDATE SET quantidade = EXCLUDED.quantidade;

-- MySQL: INSERT ON DUPLICATE KEY
INSERT INTO estoque (produto_id, quantidade) VALUES (1, 50)
ON DUPLICATE KEY UPDATE quantidade = VALUES(quantidade);

-- SQL Server: MERGE
MERGE estoque AS target
USING (VALUES (1, 50)) AS source (produto_id, quantidade)
ON target.produto_id = source.produto_id
WHEN MATCHED THEN UPDATE SET quantidade = source.quantidade
WHEN NOT MATCHED THEN INSERT (produto_id, quantidade) VALUES (source.produto_id, source.quantidade);

Lab: Data Cleaning

-- Identificar duplicatas
SELECT email, COUNT(*), MIN(id) AS manter_id
FROM clientes
GROUP BY email
HAVING COUNT(*) > 1;

-- Remover duplicatas mantendo o primeiro registro
DELETE FROM clientes
WHERE id NOT IN (
    SELECT MIN(id)
    FROM clientes
    GROUP BY email
);

-- Atualizar dados inconsistentes
UPDATE produtos
SET preco_venda = preco_custo * 1.3
WHERE preco_venda <= preco_custo;

-- Soft delete (melhor que DELETE físico)
ALTER TABLE clientes ADD COLUMN deletado_em TIMESTAMP;
UPDATE clientes SET deletado_em = CURRENT_TIMESTAMP WHERE id = 5;
-- Queries: WHERE deletado_em IS NULL

INSERT, UPDATE, DELETE são operações de escrita. Sempre use WHERE ou confirme que quer TODAS as linhas. Prefira soft delete (coluna deletado_em) a DELETE físico. TRUNCATE é mais rápido que DELETE para limpar tabelas.