SQL & Administração de Bancos de Dados
do básico ao muito avançado
Um guia completo e orientado ao mercado de trabalho: PostgreSQL, MySQL/MariaDB e SQL Server lado a lado, com exercícios resolvidos, simulados de entrevista, projetos práticos, certificações — e um capítulo inteiro sobre como a inteligência artificial está transformando a profissão.
Bancos de dados e o modelo relacional
Nível: básicoUm banco de dados é uma coleção organizada de dados que pode ser consultada, alterada e protegida de forma confiável. O software que gerencia isso é o SGBD (Sistema Gerenciador de Banco de Dados) — em inglês, DBMS. Praticamente todo sistema que você usa (banco, e-commerce, rede social, hospital) tem um ou vários SGBDs por trás.
1.1 O modelo relacional
Criado por Edgar F. Codd em 1970, o modelo relacional organiza dados em tabelas (relações), compostas por linhas (tuplas/registros) e colunas (atributos). As tabelas se relacionam por meio de chaves:
- Chave primária (PRIMARY KEY): identifica unicamente cada linha. Ex.:
id_cliente. - Chave estrangeira (FOREIGN KEY): coluna que referencia a chave primária de outra tabela, criando o relacionamento. Ex.:
pedidos.id_cliente → clientes.id_cliente. - Chave candidata / alternativa: outras colunas que também poderiam identificar a linha (ex.: CPF, e-mail) — normalmente recebem uma restrição
UNIQUE. - Chave natural vs. surrogate: chave natural tem significado no mundo real (CPF); surrogate é artificial (um número sequencial ou UUID). No mercado, a prática dominante é usar surrogate keys e proteger as naturais com
UNIQUE.
1.2 Transações e ACID
Uma transação é uma unidade de trabalho indivisível: ou tudo acontece, ou nada acontece. O clássico exemplo é a transferência bancária — debitar de uma conta e creditar em outra precisa ser atômico. As garantias de uma transação formam o acrônimo ACID:
| Propriedade | Significado | Exemplo prático |
|---|---|---|
| Atomicidade | Tudo ou nada. | Se o crédito falhar, o débito é desfeito (rollback). |
| Consistência | O banco vai de um estado válido para outro estado válido. | Constraints e regras nunca ficam violadas ao final. |
| Isolamento | Transações concorrentes não se enxergam "no meio do caminho". | Outro usuário não vê a conta com saldo intermediário. |
| Durabilidade | Depois do COMMIT, o dado sobrevive a quedas de energia. | Gravação em log de transações (WAL/redo log) antes de confirmar. |
-- Transação em SQL padrão (funciona nos três SGBDs)BEGIN; -- SQL Server: BEGIN TRANSACTION
UPDATE contas SET saldo = saldo - 500 WHERE id = 1;
UPDATE contas SET saldo = saldo + 500 WHERE id = 2;
COMMIT; -- ou ROLLBACK; para desfazer tudo
1.3 Os principais SGBDs do mercado
| SGBD | Licença | Pontos fortes | Onde aparece nas vagas |
|---|---|---|---|
| PostgreSQL | Open source | Padrão SQL rigoroso, extensões (PostGIS, pgvector), JSONB, confiabilidade | Startups, fintechs, governo, dados/IA — o queridinho da década |
| MySQL / MariaDB | Open source (Oracle / comunidade) | Simplicidade, replicação madura, enorme base instalada na web | E-commerce, web, WordPress, empresas com stack LAMP |
| SQL Server | Comercial (Microsoft) | Integração com ecossistema Microsoft, SSMS, ferramentas de BI (SSIS/SSRS) | Grandes empresas, indústria, varejo, órgãos públicos |
| Oracle Database | Comercial | Robustez extrema, RAC, recursos corporativos | Bancos, telecom, grandes ERPs |
| SQLite | Domínio público | Embutido, zero configuração | Apps mobile, dispositivos, testes |
Nas pesquisas anuais da Stack Overflow, o PostgreSQL lidera como banco mais usado e mais admirado por desenvolvedores profissionais, mas MySQL e SQL Server continuam dominando enormes bases legadas. Conclusão prática: aprenda o SQL padrão profundamente e as diferenças entre dialetos superficialmente — esta apostila marca as diferenças com selos PG MY MS.
1.4 As sublinguagens do SQL
SQL (Structured Query Language) é dividido tradicionalmente em grupos de comandos — pergunta comum em entrevista:
| Grupo | Sigla | Comandos |
|---|---|---|
| Definição de dados | DDL | CREATE, ALTER, DROP, TRUNCATE |
| Manipulação de dados | DML | INSERT, UPDATE, DELETE, MERGE |
| Consulta de dados | DQL | SELECT |
| Controle de acesso | DCL | GRANT, REVOKE |
| Controle de transação | TCL | BEGIN/START TRANSACTION, COMMIT, ROLLBACK, SAVEPOINT |
1.5 Tipos de dados essenciais
| Categoria | PostgreSQL | MySQL | SQL Server |
|---|---|---|---|
| Inteiro | SMALLINT, INT, BIGINT | iguais + TINYINT | iguais + TINYINT |
| Decimal exato (dinheiro!) | NUMERIC(12,2) | DECIMAL(12,2) | DECIMAL(12,2) |
| Ponto flutuante | REAL, DOUBLE PRECISION | FLOAT, DOUBLE | FLOAT |
| Texto | VARCHAR(n), TEXT | VARCHAR(n), TEXT | VARCHAR(n), NVARCHAR(n) (Unicode) |
| Data/hora | DATE, TIMESTAMPTZ | DATE, DATETIME, TIMESTAMP | DATE, DATETIME2, DATETIMEOFFSET |
| Booleano | BOOLEAN | TINYINT(1)/BOOL | BIT |
| Auto-incremento | GENERATED ALWAYS AS IDENTITY | AUTO_INCREMENT | IDENTITY(1,1) |
| UUID | UUID | CHAR(36)/BINARY(16) | UNIQUEIDENTIFIER |
| JSON | JSONB (indexável!) | JSON | NVARCHAR(MAX) + funções JSON (tipo nativo JSON no SQL Server 2025) |
Nunca use FLOAT/DOUBLE para dinheiro. Ponto flutuante binário não representa exatamente valores como 0,1 — os centavos "somem" em somas grandes. Use NUMERIC/DECIMAL com escala fixa.
SQL básico: consultas e manipulação
Nível: básicoUsaremos ao longo da apostila um mini-esquema de e-commerce:
-- schema de exemplo (sintaxe portável)CREATE TABLE clientes (
id INT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
cidade VARCHAR(60),
criado_em DATE DEFAULT CURRENT_DATE
);
CREATE TABLE pedidos (
id INT PRIMARY KEY,
id_cliente INT NOT NULL REFERENCES clientes(id),
valor NUMERIC(12,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pendente',
data_pedido DATE NOT NULL
);
2.1 SELECT — a espinha dorsal
SELECT nome, cidade -- quais colunas (use * só para explorar)
FROM clientes -- de qual tabela
WHERE cidade = 'São Paulo' -- filtro de linhas
ORDER BY nome ASC -- ordenação (ASC padrão, DESC inverso)
LIMIT 10; -- PG/MySQL. SQL Server: SELECT TOP 10 ...
-- padrão ANSI: FETCH FIRST 10 ROWS ONLY
Operadores de filtro mais usados
| Operador | Exemplo | Observação |
|---|---|---|
= <> > < >= <= | valor >= 100 | <> é "diferente" (também !=) |
BETWEEN | valor BETWEEN 50 AND 100 | Inclusivo nas duas pontas |
IN | status IN ('pago','enviado') | Lista de valores |
LIKE | nome LIKE 'Ma%' | % = qualquer sequência; _ = 1 caractere. PG: ILIKE ignora maiúsculas |
IS NULL / IS NOT NULL | cidade IS NULL | Nunca use = NULL — não funciona! |
AND / OR / NOT | a AND (b OR c) | Use parênteses; AND tem precedência sobre OR |
NULL significa "valor desconhecido/ausente", não zero nem string vazia. Qualquer comparação com NULL resulta em desconhecido (lógica de três valores). Por isso WHERE cidade <> 'SP' não retorna linhas com cidade NULL. Funções úteis: COALESCE(x, 'padrão') devolve o primeiro valor não nulo — pergunta frequentíssima de entrevista.
2.2 Funções de linha essenciais
-- Texto
SELECT UPPER(nome), LOWER(email), LENGTH(nome), -- MS: LEN()
SUBSTRING(nome, 1, 3), TRIM(nome),
CONCAT(nome, ' - ', cidade), REPLACE(email, '@', ' arroba ')
FROM clientes;
-- Datas (cada banco tem seu tempero)
SELECT CURRENT_DATE; -- PG/MySQL; MS: CAST(GETDATE() AS DATE)
SELECT EXTRACT(YEAR FROM data_pedido); -- PG/MySQL; MS: YEAR(data_pedido)
SELECT data_pedido + INTERVAL '7 days'; -- PG; MySQL: DATE_ADD(d, INTERVAL 7 DAY)
-- MS: DATEADD(DAY, 7, data_pedido)
-- Condicional (padrão ANSI, funciona em todos)
SELECT valor,
CASE WHEN valor >= 1000 THEN 'alto'
WHEN valor >= 200 THEN 'médio'
ELSE 'baixo' END AS faixa
FROM pedidos;
2.3 INSERT, UPDATE e DELETE
-- Inserir
INSERT INTO clientes (id, nome, email, cidade)
VALUES (1, 'Ana Souza', 'ana@ex.com', 'São Paulo'),
(2, 'Bruno Lima', 'bruno@ex.com', 'Recife');
-- Atualizar (SEMPRE com WHERE, salvo intenção explícita!)
UPDATE pedidos SET status = 'pago' WHERE id = 42;
-- Excluir
DELETE FROM pedidos WHERE status = 'cancelado' AND data_pedido < DATE '2024-01-01';
Antes de rodar UPDATE/DELETE em produção: (1) rode um SELECT COUNT(*) com o mesmo WHERE para conferir quantas linhas serão afetadas; (2) abra transação (BEGIN) e só dê COMMIT após verificar; (3) tenha backup. Histórias de "esqueci o WHERE" encerram carreiras — e alimentam o capítulo 6 sobre backups.
2.4 DISTINCT, aliases e ordem lógica
SELECT DISTINCT cidade FROM clientes; -- valores únicos
SELECT nome AS cliente, valor * 1.1 AS valor_com_taxa
FROM pedidos p JOIN clientes c ON c.id = p.id_cliente; -- alias de tabela: p, c
A ordem lógica de execução de um SELECT (diferente da ordem escrita!) é pergunta clássica de entrevista:
FROM → JOIN/ON → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
É por isso que você não pode usar um alias do SELECT dentro do WHERE (o WHERE executa antes) — mas pode no ORDER BY.
SQL intermediário: JOINs, agregações e DDL
Nível: intermediário3.1 JOINs — combinando tabelas
JOINs são o coração do SQL relacional e o assunto nº 1 em testes técnicos.
| JOIN | O que retorna | Uso típico |
|---|---|---|
INNER JOIN | Somente linhas com correspondência nas duas tabelas | Pedidos com seus clientes |
LEFT JOIN | Todas as linhas da esquerda + correspondências da direita (NULL onde não há) | Todos os clientes, tenham ou não pedidos |
RIGHT JOIN | Espelho do LEFT | Raro; prefira reescrever como LEFT |
FULL OUTER JOIN | Tudo dos dois lados | Conciliações. MySQL não tem — simule com LEFT UNION RIGHT |
CROSS JOIN | Produto cartesiano (todas as combinações) | Gerar grades/combinações |
| Self join | Tabela com ela mesma | Funcionário → gerente |
-- Clientes SEM nenhum pedido (padrão de entrevista: anti-join)
SELECT c.nome
FROM clientes c
LEFT JOIN pedidos p ON p.id_cliente = c.id
WHERE p.id IS NULL;
-- Self join: funcionário e seu gerente
SELECT f.nome AS funcionario, g.nome AS gerente
FROM funcionarios f
LEFT JOIN funcionarios g ON g.id = f.id_gerente;
Colocar filtro da tabela da direita no WHERE transforma um LEFT JOIN em INNER JOIN silenciosamente: WHERE p.status = 'pago' elimina os NULLs. Para manter o LEFT, mova o filtro para o ON: LEFT JOIN pedidos p ON p.id_cliente = c.id AND p.status = 'pago'.
3.2 Agregações: GROUP BY e HAVING
-- Total gasto por cliente em 2025, só quem gastou mais de R$ 1.000
SELECT c.nome,
COUNT(*) AS qtd_pedidos,
SUM(p.valor) AS total,
AVG(p.valor) AS ticket_medio,
MIN(p.data_pedido) AS primeiro,
MAX(p.data_pedido) AS ultimo
FROM pedidos p
JOIN clientes c ON c.id = p.id_cliente
WHERE p.data_pedido >= DATE '2025-01-01' -- filtra LINHAS antes de agrupar
GROUP BY c.nome
HAVING SUM(p.valor) > 1000 -- filtra GRUPOS após agregar
ORDER BY total DESC;
COUNT(*)conta linhas;COUNT(coluna)ignora NULLs;COUNT(DISTINCT coluna)conta valores únicos.- Regra de ouro: toda coluna no SELECT que não está agregada deve estar no GROUP BY (MySQL antigo tolerava violar isso — modo
ONLY_FULL_GROUP_BYcorrige). WHEREnão pode usar agregação;HAVINGpode. Diferença WHERE × HAVING = pergunta garantida em entrevista.
3.3 Subconsultas
-- Escalar: pedidos acima da média geral
SELECT * FROM pedidos
WHERE valor > (SELECT AVG(valor) FROM pedidos);
-- IN / NOT IN (cuidado: NOT IN com NULL retorna vazio!)
SELECT nome FROM clientes
WHERE id IN (SELECT id_cliente FROM pedidos WHERE valor > 500);
-- EXISTS correlacionada (geralmente mais eficiente e à prova de NULL)
SELECT nome FROM clientes c
WHERE EXISTS (SELECT 1 FROM pedidos p WHERE p.id_cliente = c.id);
-- Tabela derivada no FROM
SELECT faixa, COUNT(*)
FROM (SELECT CASE WHEN valor >= 1000 THEN 'alto' ELSE 'normal' END AS faixa
FROM pedidos) t
GROUP BY faixa;
3.4 Operações de conjunto
SELECT email FROM clientes_loja_a
UNION -- une e remove duplicatas; UNION ALL mantém (e é mais rápido)
SELECT email FROM clientes_loja_b;
-- INTERSECT (em ambos) e EXCEPT (no 1º e não no 2º)
-- PG e SQL Server: nativos. MySQL: a partir do 8.0.31.
3.5 DDL: criando e alterando estruturas
CREATE TABLE produtos (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- PG
-- MySQL: id BIGINT AUTO_INCREMENT PRIMARY KEY
-- MS: id BIGINT IDENTITY(1,1) PRIMARY KEY
nome VARCHAR(120) NOT NULL,
preco NUMERIC(10,2) NOT NULL CHECK (preco >= 0),
sku VARCHAR(30) UNIQUE NOT NULL,
id_categoria INT REFERENCES categorias(id) ON DELETE SET NULL
);
ALTER TABLE produtos ADD COLUMN estoque INT DEFAULT 0;
ALTER TABLE produtos DROP COLUMN estoque;
DROP TABLE IF EXISTS produtos_temp;
TRUNCATE TABLE log_acessos; -- esvazia rápido, sem WHERE, quase sem log
Ações referenciais (ON DELETE / ON UPDATE)
RESTRICT/NO ACTION— impede excluir pai com filhos (padrão seguro).CASCADE— exclui os filhos junto (use com muita consciência).SET NULL / SET DEFAULT— órfãos recebem NULL/valor padrão.
3.6 Views
CREATE VIEW vw_vendas_mensais AS
SELECT DATE_TRUNC('month', data_pedido) AS mes, -- PG; MySQL: DATE_FORMAT; MS: DATETRUNC (2022+)
SUM(valor) AS total
FROM pedidos GROUP BY 1;
Views encapsulam consultas complexas, padronizam regras de negócio e controlam acesso (o usuário vê a view, não as tabelas). Views materializadas (PG nativo com REFRESH MATERIALIZED VIEW; MS como indexed views) gravam o resultado fisicamente — ótimas para dashboards.
SQL avançado: window functions, CTEs e recursos modernos
Nível: avançado4.1 Window functions — o divisor de águas
Funções de janela calculam agregações sem colapsar as linhas. Dominar isso separa candidatos júnior de pleno/sênior em quase todo teste de SQL para dados.
SELECT
id_cliente, data_pedido, valor,
SUM(valor) OVER (PARTITION BY id_cliente) AS total_cliente,
SUM(valor) OVER (PARTITION BY id_cliente ORDER BY data_pedido) AS acumulado,
ROW_NUMBER() OVER (PARTITION BY id_cliente ORDER BY valor DESC) AS posicao,
RANK() OVER (ORDER BY valor DESC) AS rank_geral, -- empata: 1,1,3
DENSE_RANK() OVER (ORDER BY valor DESC) AS dense_geral, -- empata: 1,1,2
LAG(valor) OVER (PARTITION BY id_cliente ORDER BY data_pedido) AS valor_anterior,
LEAD(data_pedido) OVER (PARTITION BY id_cliente ORDER BY data_pedido) AS proximo_pedido,
NTILE(4) OVER (ORDER BY valor) AS quartil
FROM pedidos;
-- Padrão de entrevista: "o pedido mais recente de cada cliente"
SELECT * FROM (
SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY id_cliente ORDER BY data_pedido DESC) AS rn
FROM pedidos p
) t WHERE rn = 1;
-- Média móvel de 7 dias (frames de janela)
SELECT data_pedido,
AVG(valor) OVER (ORDER BY data_pedido
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS mm7
FROM vendas_diarias;
4.2 CTEs (WITH) e recursividade
-- CTE: organiza consultas complexas em etapas nomeadas
WITH vendas_2025 AS (
SELECT id_cliente, SUM(valor) AS total
FROM pedidos WHERE data_pedido >= DATE '2025-01-01'
GROUP BY id_cliente
),
media AS (SELECT AVG(total) AS m FROM vendas_2025)
SELECT v.* FROM vendas_2025 v, media WHERE v.total > media.m;
-- CTE recursiva: hierarquia de funcionários (org chart)
WITH RECURSIVE hierarquia AS ( -- MS: sem a palavra RECURSIVE
SELECT id, nome, id_gerente, 1 AS nivel
FROM funcionarios WHERE id_gerente IS NULL
UNION ALL
SELECT f.id, f.nome, f.id_gerente, h.nivel + 1
FROM funcionarios f
JOIN hierarquia h ON f.id_gerente = h.id
)
SELECT * FROM hierarquia ORDER BY nivel;
4.3 UPSERT e MERGE
-- PostgreSQL
INSERT INTO estoque (sku, qtd) VALUES ('ABC-1', 10)
ON CONFLICT (sku) DO UPDATE SET qtd = estoque.qtd + EXCLUDED.qtd;
-- MySQL
INSERT INTO estoque (sku, qtd) VALUES ('ABC-1', 10)
ON DUPLICATE KEY UPDATE qtd = qtd + VALUES(qtd);
-- SQL Server (e PG 15+): MERGE — padrão em pipelines de dados
MERGE INTO estoque AS alvo
USING novas_cargas AS fonte ON alvo.sku = fonte.sku
WHEN MATCHED THEN UPDATE SET qtd = alvo.qtd + fonte.qtd
WHEN NOT MATCHED THEN INSERT (sku, qtd) VALUES (fonte.sku, fonte.qtd);
4.4 JSON dentro do banco relacional
-- PostgreSQL (JSONB: binário, indexável com GIN)
SELECT dados->>'nome' AS nome, dados->'endereco'->>'cidade' AS cidade
FROM eventos WHERE dados @> '{"tipo":"compra"}';
-- MySQL
SELECT JSON_EXTRACT(dados, '$.nome'), dados->>'$.endereco.cidade' FROM eventos;
-- SQL Server
SELECT JSON_VALUE(dados, '$.nome') FROM eventos;
SELECT * FROM OPENJSON(@json) WITH (nome VARCHAR(100) '$.nome');
O padrão SQL:2023 consolidou o tipo JSON e funções como JSON_TABLE — a fronteira entre "relacional" e "documento" praticamente desapareceu nos três SGBDs.
4.5 Stored procedures, funções e triggers
-- Função em PL/pgSQL (PostgreSQL)
CREATE OR REPLACE FUNCTION total_cliente(p_id INT) RETURNS NUMERIC AS $$
BEGIN
RETURN (SELECT COALESCE(SUM(valor), 0) FROM pedidos WHERE id_cliente = p_id);
END; $$ LANGUAGE plpgsql;
-- Trigger de auditoria
CREATE OR REPLACE FUNCTION fn_audita_preco() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO auditoria_precos(id_produto, preco_antigo, preco_novo, alterado_em)
VALUES (OLD.id, OLD.preco, NEW.preco, NOW());
RETURN NEW;
END; $$ LANGUAGE plpgsql;
CREATE TRIGGER trg_audita_preco
BEFORE UPDATE OF preco ON produtos
FOR EACH ROW EXECUTE FUNCTION fn_audita_preco();
Equivalentes: MySQL usa DELIMITER + CREATE PROCEDURE/TRIGGER; MS usa T-SQL (CREATE PROCEDURE ... AS BEGIN ... END, triggers AFTER/INSTEAD OF). No mercado atual a lógica de negócio migrou majoritariamente para a aplicação, mas procedures seguem fortes em legados corporativos, cargas de dados e times SQL Server/Oracle — saiba ler e manter.
4.6 Níveis de isolamento e concorrência
| Nível | Evita | Anomalias possíveis |
|---|---|---|
READ UNCOMMITTED | — | Leitura suja, não repetível, fantasma |
READ COMMITTED (padrão PG, MS, Oracle) | Leitura suja | Não repetível, fantasma |
REPEATABLE READ (padrão MySQL/InnoDB) | + leitura não repetível | Fantasma (no InnoDB, quase eliminado por gap locks) |
SERIALIZABLE | Todas | — (custo: mais bloqueios/aborts) |
PG e MySQL/InnoDB usam MVCC (multiversão): leitores não bloqueiam escritores. MS tradicionalmente usa locks, mas oferece READ_COMMITTED_SNAPSHOT para comportamento similar. Deadlock (duas transações esperando uma pela outra) é resolvido pelo SGBD matando uma delas — a aplicação deve estar pronta para repetir a transação.
Modelagem de dados e normalização
Nível: intermediário5.1 Do requisito ao modelo
- Modelo conceitual — diagrama entidade-relacionamento (ER): entidades, atributos, cardinalidades (1:1, 1:N, N:N).
- Modelo lógico — tabelas, colunas, chaves; N:N vira tabela associativa (ex.:
pedido_itens). - Modelo físico — tipos concretos, índices, particionamento, específicos do SGBD.
5.2 Formas normais (o essencial que caem em prova e entrevista)
| Forma | Regra | Cheiro de violação |
|---|---|---|
| 1FN | Valores atômicos; sem grupos repetidos | Coluna telefones = '9999, 8888' |
| 2FN | 1FN + todo atributo depende da chave inteira | Em pedido_itens(pedido, produto, nome_produto), o nome depende só de produto |
| 3FN | 2FN + sem dependência transitiva | clientes(id, cidade, uf_da_cidade) — UF depende da cidade, não do id |
| BCNF | Toda dependência parte de uma chave candidata | Casos raros com múltiplas chaves compostas sobrepostas |
Por que normalizar? Eliminar redundância e anomalias de inserção/atualização/exclusão. Por que desnormalizar? Performance de leitura em relatórios — decisão consciente, documentada, geralmente em data warehouses.
5.3 Modelagem analítica (mundo dos dados)
- Star schema: tabela fato (vendas: métricas + FKs) cercada de dimensões (data, produto, cliente, loja). Base do BI e assunto certo em vagas de engenharia/análise de dados.
- Snowflake: dimensões normalizadas em subtabelas.
- SCD (Slowly Changing Dimensions): Tipo 1 sobrescreve; Tipo 2 versiona com
data_inicio/data_fim/flag_atual— pergunta recorrente em entrevistas de dados. - OLTP × OLAP: transacional (muitas escritas pequenas, normalizado) × analítico (leituras massivas, colunar/desnormalizado).
-- Fato + dimensão (star schema mínimo)
CREATE TABLE dim_data (sk_data INT PRIMARY KEY, data DATE, ano INT, mes INT, dia_semana VARCHAR(10));
CREATE TABLE dim_produto (sk_produto INT PRIMARY KEY, sku VARCHAR(30), nome VARCHAR(120), categoria VARCHAR(60));
CREATE TABLE fato_vendas (
sk_data INT REFERENCES dim_data,
sk_produto INT REFERENCES dim_produto,
quantidade INT, receita NUMERIC(14,2)
);
"Modelagem dimensional", "Kimball", "camadas bronze/silver/gold (medallion)", "dbt". Saber explicar quando normalizar (OLTP) e quando desnormalizar (OLAP) com um exemplo concreto vale mais que decorar a definição formal de BCNF.
Administração: o dia a dia do DBA
Nível: avançado6.1 Arquitetura interna (o que todo DBA precisa visualizar)
- Memória: cache de páginas de dados — PG
shared_buffers, MYinnodb_buffer_pool_size, MS buffer pool (gerenciado +max server memory). Regra prática: buffer pool grande o bastante para o "working set" caber em RAM. - Log de transações (write-ahead): PG WAL, MY redo log, MS transaction log. Toda alteração é escrita no log antes dos arquivos de dados — é isso que garante durabilidade e permite recuperação point-in-time.
- Checkpoint: momento em que páginas sujas da memória são gravadas em disco.
- Processos/threads: PG usa um processo por conexão (por isso o PgBouncer — pooler de conexões — é quase obrigatório em produção); MySQL e SQL Server usam threads.
6.2 Usuários, papéis e permissões
-- PostgreSQL: roles unificam usuário e grupo
CREATE ROLE leitura_app NOLOGIN;
GRANT USAGE ON SCHEMA public TO leitura_app;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO leitura_app;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO leitura_app;
CREATE USER maria WITH PASSWORD 'S3nh@Forte!' IN ROLE leitura_app;
-- MySQL
CREATE USER 'maria'@'%' IDENTIFIED BY 'S3nh@Forte!';
GRANT SELECT ON loja.* TO 'maria'@'%';
-- SQL Server: login (servidor) + user (banco) + role
CREATE LOGIN maria WITH PASSWORD = 'S3nh@Forte!';
CREATE USER maria FOR LOGIN maria;
ALTER ROLE db_datareader ADD MEMBER maria;
Princípio do menor privilégio: a aplicação nunca conecta como superusuário; cada serviço tem seu papel com o mínimo necessário. REVOKE remove permissões.
6.3 Backup e recuperação — a habilidade que define o DBA
| Conceito | Significado |
|---|---|
| RPO (Recovery Point Objective) | Quanto de dado você aceita perder (ex.: 5 min) |
| RTO (Recovery Time Objective) | Em quanto tempo o sistema deve voltar (ex.: 1 h) |
| Backup lógico | Exporta comandos/dados (pg_dump, mysqldump/mysqlpump, bacpac) — portátil, mais lento para restaurar |
| Backup físico | Copia arquivos/páginas (pg_basebackup, Percona XtraBackup, BACKUP DATABASE) — rápido, base da recuperação séria |
| PITR (point-in-time recovery) | Backup físico + arquivamento contínuo do log (WAL archiving / binlog / log backups) → restaurar para "ontem 14:32, um segundo antes do DELETE errado" |
# PostgreSQL
pg_dump -Fc -d loja -f loja.dump # lógico, formato custom
pg_restore -d loja_nova loja.dump
pg_basebackup -D /backup/base -X stream # físico (base para PITR)
# MySQL
mysqldump --single-transaction --routines loja > loja.sql
xtrabackup --backup --target-dir=/backup/full
# SQL Server (T-SQL)
BACKUP DATABASE loja TO DISK = 'D:\bkp\loja_full.bak' WITH COMPRESSION, CHECKSUM;
BACKUP LOG loja TO DISK = 'D:\bkp\loja_log.trn';
RESTORE DATABASE loja FROM DISK = 'D:\bkp\loja_full.bak' WITH NORECOVERY;
RESTORE LOG loja FROM DISK = 'D:\bkp\loja_log.trn' WITH STOPAT = '2026-07-09 14:32';
Backup que nunca foi restaurado em teste não é backup — é esperança. Agende restaurações de teste periódicas e documente o runbook. Estratégia 3-2-1: 3 cópias, 2 mídias diferentes, 1 fora do site (hoje: outra região de cloud, com cópia imutável contra ransomware).
6.4 Replicação e alta disponibilidade (HA)
- Replicação física/streaming PG: réplicas leem o WAL do primário; síncrona (RPO=0, mais latência) ou assíncrona. Failover automatizado com Patroni + etcd — stack padrão de mercado.
- Replicação lógica PG (
PUBLICATION/SUBSCRIPTION): replica tabelas específicas, permite upgrades com quase zero downtime e integrações CDC. - MySQL: replicação por binlog (assíncrona/semissíncrona), Group Replication/InnoDB Cluster para HA; GTID simplifica failover.
- MS: Always On Availability Groups (réplicas síncronas/assíncronas, listener para failover), log shipping como opção simples.
- Leituras nas réplicas escalam relatórios; atenção ao replication lag (leitura desatualizada logo após escrita).
6.5 Manutenção de rotina
- PG: VACUUM/autovacuum limpa versões mortas do MVCC e previne transaction ID wraparound;
ANALYZEatualiza estatísticas; monitore bloat. - MY:
ANALYZE TABLE,OPTIMIZE TABLEocasional, purga de binlogs. - MS: rebuild/reorganize de índices conforme fragmentação,
UPDATE STATISTICS,DBCC CHECKDB(integridade) — os scripts de Ola Hallengren são padrão de fato. - Monitoramento: conexões, locks, queries lentas, espaço em disco, lag de replicação. Ferramentas:
pg_stat_statements, Performance Schema, Query Store; Prometheus + Grafana; Zabbix; Datadog.
6.6 O DBA moderno: infraestrutura como código
O mercado espera cada vez mais que o DBA opere como engenheiro de plataforma: bancos provisionados via Terraform, mudanças de schema versionadas com Flyway/Liquibase (migrations em CI/CD), bancos em Kubernetes com operadores (CloudNativePG, Zalando), e observabilidade centralizada. "Clicar no console" virou exceção; tudo é script, versionado e auditável.
Performance e tuning
Nível: avançado7.1 Índices — a alavanca nº 1
Um índice é uma estrutura auxiliar (quase sempre B-tree) que permite localizar linhas sem varrer a tabela inteira. Custo: espaço + escrita mais lenta (cada INSERT/UPDATE atualiza os índices).
CREATE INDEX idx_pedidos_cliente ON pedidos (id_cliente);
CREATE INDEX idx_pedidos_status_data ON pedidos (status, data_pedido); -- composto
CREATE UNIQUE INDEX idx_clientes_email ON clientes (email);
-- PostgreSQL: recursos extras muito cobrados em vagas
CREATE INDEX idx_ativos ON pedidos (data_pedido) WHERE status = 'pendente'; -- parcial
CREATE INDEX idx_email_lower ON clientes (LOWER(email)); -- funcional
CREATE INDEX idx_dados_gin ON eventos USING gin (dados); -- GIN p/ JSONB
CREATE INDEX idx_cobertura ON pedidos (id_cliente) INCLUDE (valor); -- covering (PG/MS)
- Regra do prefixo esquerdo: índice
(status, data_pedido)serve para filtro porstatusou porstatus+data, mas não pordata_pedidosozinho. - Seletividade: índice em coluna com 2 valores (ex.: sexo) raramente ajuda; em e-mail único, ajuda muito.
- Sargability: função sobre a coluna mata o índice —
WHERE YEAR(data)=2025vira scan; escrevaWHERE data >= '2025-01-01' AND data < '2026-01-01'. - MS: índice clustered define a ordem física da tabela (1 por tabela); os demais são nonclustered. MY/InnoDB: a PK é sempre o índice clustered. PG: heap tables (sem clustered permanente).
- Columnstore (MS) e extensões colunar/analíticas: compressão + varredura vetorizada para OLAP.
7.2 Lendo planos de execução
-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS) SELECT ... ;
-- MySQL
EXPLAIN ANALYZE SELECT ... ;
-- SQL Server: "Include Actual Execution Plan" no SSMS ou SET STATISTICS IO, TIME ON
| Operador no plano | Leitura |
|---|---|
| Seq Scan / Table Scan / Full Scan | Varredura completa — ok em tabelas pequenas ou quando retorna grande fração das linhas; suspeito com WHERE seletivo |
| Index Scan / Index Seek | Uso do índice — o que você geralmente quer para buscas pontuais |
| Nested Loop | Bom para poucos registros externos com índice no interno |
| Hash Join | Bom para volumes grandes sem ordenação |
| Merge Join | Ótimo quando as duas entradas já estão ordenadas |
| Sort / Filter caros, "rows" estimado ≠ real | Estatísticas desatualizadas → ANALYZE/UPDATE STATISTICS |
7.3 Metodologia de tuning (roteiro de entrevista sênior)
- Meça: identifique as queries mais custosas —
pg_stat_statements,slow_query_log, Query Store. - Explique: rode EXPLAIN ANALYZE; compare linhas estimadas × reais.
- Aja na ordem de custo/benefício: reescrever a query (eliminar SELECT *, funções em colunas filtradas, subquery correlacionada desnecessária) → criar/ajustar índice → atualizar estatísticas → só então mexer em parâmetros do servidor → hardware por último.
- Reavalie e registre o antes/depois.
7.4 Particionamento
-- PostgreSQL: particionamento declarativo por intervalo
CREATE TABLE vendas (
id BIGINT, data_venda DATE NOT NULL, valor NUMERIC(12,2)
) PARTITION BY RANGE (data_venda);
CREATE TABLE vendas_2026_07 PARTITION OF vendas
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
Benefícios: partition pruning (o otimizador ignora partições fora do filtro), expurgo instantâneo (DROP/DETACH PARTITION em vez de DELETE gigante), manutenção por pedaço. MySQL e SQL Server têm particionamento equivalente (RANGE/LIST/HASH; partition function/scheme no MS).
7.5 Parâmetros que mais movem o ponteiro
| Objetivo | PostgreSQL | MySQL | SQL Server |
|---|---|---|---|
| Cache de dados | shared_buffers (~25% RAM) + effective_cache_size | innodb_buffer_pool_size (~70% RAM dedicada) | max server memory |
| Memória por operação | work_mem | sort_buffer_size etc. | memory grants (automático) |
| Escrita/log | wal_compression, max_wal_size | innodb_redo_log_capacity, innodb_flush_log_at_trx_commit | posicionar log em disco rápido; VLFs saudáveis |
| Paralelismo | max_parallel_workers_per_gather | (limitado) | MAXDOP, cost threshold for parallelism |
Complete o quadro com caching externo (Redis para leituras quentes), connection pooling (PgBouncer/ProxySQL) e read replicas — escalar leitura horizontalmente é quase sempre mais barato que "engordar" o primário.
Segurança e LGPD
Nível: avançado8.1 SQL Injection — o ataque nº 1 (ainda)
-- VULNERÁVEL: concatenar entrada do usuário
query = "SELECT * FROM usuarios WHERE email = '" + email + "'"
-- entrada maliciosa: ' OR '1'='1' -- → devolve todos os usuários
-- CORRETO: consultas parametrizadas (prepared statements)
cursor.execute("SELECT * FROM usuarios WHERE email = %s", (email,)) # Python/psycopg
-- O driver envia SQL e dados separados; a entrada nunca é interpretada como código.
Defesa em profundidade: parametrização sempre + usuário da aplicação com privilégios mínimos + validação de entrada + WAF. ORMs não imunizam se você concatenar strings em "raw queries".
8.2 Criptografia
- Em trânsito: TLS obrigatório (
sslmode=require/verify-fullno PG;Encrypt=Trueno MS). - Em repouso: TDE (Transparent Data Encryption) no SQL Server/Oracle/MySQL Enterprise; no PG, criptografia de disco/volume ou extensões; nas clouds, criptografia de storage é padrão com chaves gerenciáveis (KMS).
- Em nível de coluna:
pgcrypto,AES_ENCRYPT, Always Encrypted (MS — nem o DBA vê o dado). - Senhas de usuários finais: nunca criptografe-as de forma reversível — armazene hash forte (bcrypt/argon2) na aplicação.
8.3 Controles finos de acesso
-- Row-Level Security (PostgreSQL): cada vendedor só vê suas linhas
ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY;
CREATE POLICY p_vendedor ON pedidos
USING (id_vendedor = current_setting('app.vendedor_id')::INT);
-- SQL Server: CREATE SECURITY POLICY; MySQL: emular com views
-- Mascaramento dinâmico (SQL Server)
ALTER TABLE clientes ALTER COLUMN cpf
ADD MASKED WITH (FUNCTION = 'partial(0,"***.***.***-",2)');
8.4 LGPD na prática do banco de dados
- Inventário de dados pessoais: saiba em quais tabelas/colunas há CPF, e-mail, saúde, biometria (dados sensíveis exigem mais rigor).
- Minimização e retenção: colete o necessário e defina expurgo (jobs de purge/anonimização; particionamento ajuda).
- Direitos do titular: processos para exportar (portabilidade) e apagar/anonimizar dados de uma pessoa — pense nisso na modelagem (FKs, backups!).
- Anonimização × pseudonimização: anonimizado de verdade sai do escopo da LGPD; pseudonimizado (ex.: ID substituto com tabela de correspondência) continua dado pessoal.
- Auditoria: registre quem acessou/alterou o quê —
pgaudit, MySQL Enterprise Audit / MariaDB Audit Plugin, SQL Server Audit. - Ambientes de teste: nunca copie produção "crua" para dev — use mascaramento/geração sintética.
Multas da LGPD chegam a 2% do faturamento (até R$ 50 milhões por infração). Profissionais de banco que falam a língua de privacidade por design, mascaramento e auditoria têm vantagem clara em bancos, saúde e varejo.
O ecossistema moderno: cloud, NoSQL, NewSQL e além
Nível: muito avançado9.1 Bancos gerenciados na nuvem — onde o mercado está
| Nuvem | Serviços principais | O que saber |
|---|---|---|
| AWS | RDS (PG/MySQL/MS/Oracle), Aurora (PG/MySQL com storage distribuído), Aurora Serverless, DynamoDB, Redshift | Aurora separa computação de storage (6 cópias em 3 AZs); Serverless v2 escala por ACUs |
| Azure | Azure SQL Database/Managed Instance, Database for PostgreSQL/MySQL (Flexible Server), Cosmos DB, Synapse/Fabric | Forte onde já há ecossistema Microsoft; Hyperscale para bases enormes |
| Google Cloud | Cloud SQL, AlloyDB (PG turbinado, colunar em memória), Spanner, BigQuery | Spanner = SQL distribuído global com consistência forte |
| Independentes | Neon (PG serverless, branching de banco), Supabase (PG + APIs), PlanetScale (MySQL/Vitess), TiDB, CockroachDB, Snowflake, Databricks | Branching de banco (criar cópia instantânea por branch de código) virou padrão em times modernos |
O que muda para o DBA na nuvem: some o trabalho de instalar/patchear/backup manual; cresce o trabalho de arquitetura, custo (FinOps!), segurança de rede (VPC, IAM), réplicas multi-região e tuning fino — o provedor não otimiza suas queries por você.
9.2 NoSQL — quando o relacional não é a melhor ferramenta
| Tipo | Exemplos | Caso de uso |
|---|---|---|
| Documento | MongoDB, Couchbase, Firestore | Esquema flexível, catálogos, perfis |
| Chave-valor | Redis, Valkey, DynamoDB | Cache, sessões, filas leves, latência de microssegundos |
| Colunar-wide | Cassandra, ScyllaDB, HBase | Escrita massiva distribuída, séries de eventos |
| Grafos | Neo4j, Neptune | Relacionamentos profundos: fraude, redes, recomendação |
| Séries temporais | TimescaleDB (PG!), InfluxDB, ClickHouse | Métricas, IoT, telemetria |
| Busca | Elasticsearch/OpenSearch | Full-text, logs, facetas |
Teorema CAP (pergunta clássica): num sistema distribuído sob partição de rede, escolha entre Consistência e Disponibilidade. NoSQL costuma preferir disponibilidade + consistência eventual; relacionais e NewSQL preferem consistência. Resposta madura em entrevista: "não é SQL versus NoSQL — é persistência poliglota: Postgres como fonte de verdade, Redis para cache, OpenSearch para busca."
9.3 NewSQL / SQL distribuído e HTAP
- NewSQL: escala horizontal do NoSQL + ACID e SQL do relacional — Spanner, CockroachDB, TiDB, YugabyteDB. Baseiam-se em consenso distribuído (Raft/Paxos) e relógios sincronizados.
- HTAP (Hybrid Transactional/Analytical): transação e análise na mesma plataforma — TiDB + TiFlash, AlloyDB, SingleStore, Oracle In-Memory.
- OLAP moderno: Snowflake, BigQuery, Redshift, Databricks (lakehouse), ClickHouse e o fenômeno DuckDB ("SQLite analítico" — análise colunar embutida, queridinho de dados). Formatos abertos Parquet + Apache Iceberg/Delta Lake viraram a "tabela" do data lake, consultável por SQL de vários motores.
- CDC (Change Data Capture): Debezium lê o log de transações e transmite mudanças via Kafka — é assim que o operacional alimenta o analítico em tempo real.
9.4 Recursos modernos dentro dos próprios SGBDs
- PG: JSONB, arrays, tipos range, full-text search, PostGIS (geoespacial), TimescaleDB, pgvector (cap. 10), FDW (consultar outros bancos como tabelas), logical replication,
MERGE,JSON_TABLE(PG 17). - MY: InnoDB Cluster, roles, índices invisíveis e funcionais, histogramas, HeatWave (analítico+ML na Oracle Cloud).
- MS: columnstore, In-Memory OLTP, Query Store, Intelligent Query Processing (feedback automático de memória/cardinalidade), ledger tables (imutabilidade verificável), integração com Fabric.
- SQL:2023: property graph queries (consultas de grafo em SQL!), JSON aprimorado — o padrão continua absorvendo os vizinhos.
Inteligência Artificial e bancos de dados: o que muda (e o que não muda)
Nível: muito avançado · visão de futuro10.1 IA como usuária do banco: text-to-SQL e copilotos
LLMs (como GPT, Claude e Gemini) escrevem SQL a partir de linguagem natural com qualidade crescente. Isso já aparece em produto: Copilot no SSMS/Azure Data Studio, assistentes em Snowflake (Cortex Analyst), Databricks (Genie), BigQuery (Gemini), e em ferramentas de BI. Consequências práticas:
- O SQL trivial foi comoditizado. "SELECT com JOIN e GROUP BY" a IA escreve em segundos. O valor humano migra para: validar se a query está correta para o negócio, garantir performance e segurança, e modelar dados que a IA consiga entender.
- Metadados viram ouro. Text-to-SQL só funciona bem com schemas bem nomeados, comentários (
COMMENT ON TABLE/COLUMN), dicionário de dados e camada semântica (métricas definidas uma vez — dbt semantic layer, LookML). Documentar o banco deixou de ser burocracia: é o que torna seus dados "IA-consultáveis". - Risco: queries geradas podem estar sutilmente erradas (JOIN que duplica linhas, filtro de data equivocado) ou perigosas. Boas práticas: conceder à IA apenas usuário read-only, com timeout, limites de linhas, RLS e revisão humana para tudo que vira decisão.
10.2 O banco como memória da IA: vetores e RAG
A onda mais concreta: busca vetorial. Textos/imagens viram embeddings (vetores de centenas/milhares de dimensões); buscar "significado parecido" = buscar vetores próximos. É a base do RAG (Retrieval-Augmented Generation): o app busca os trechos mais relevantes no banco e os entrega ao LLM para responder com dados da empresa.
-- PostgreSQL + pgvector: o banco relacional virou banco vetorial
CREATE EXTENSION vector;
CREATE TABLE documentos (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
conteudo TEXT,
embedding vector(1536) -- dimensão do modelo de embedding
);
-- índice aproximado (HNSW) para busca em milhões de vetores
CREATE INDEX ON documentos USING hnsw (embedding vector_cosine_ops);
-- os 5 documentos mais similares à pergunta do usuário
SELECT id, conteudo
FROM documentos
ORDER BY embedding <=> :embedding_da_pergunta -- <=> distância de cosseno
LIMIT 5;
- Equivalentes: MySQL 9 tem tipo
VECTOR; MS SQL Server 2025 introduz tipo vetorial e busca nativa; Oracle 23ai idem. Bancos vetoriais dedicados: Pinecone, Milvus, Qdrant, Weaviate — mas "Postgres com pgvector" venceu enorme fatia do mercado por simplicidade. - Busca híbrida (vetor + full-text/BM25 combinados) é o padrão de qualidade em RAG sério.
- Novo trabalho de DBA: dimensionar índices HNSW/IVFFlat (RAM!), medir recall × latência, versionar embeddings quando o modelo muda.
10.3 IA dentro do SGBD: bancos autônomos e tuning por ML
- Oracle Autonomous Database: patch, tuning e indexação automáticos ("self-driving").
- SQL Server: Automatic Tuning (força o último plano bom em regressões), Intelligent Query Processing.
- Nuvem: recomendadores de índice (Azure/AWS/GCP), Aurora com previsão de capacidade, autoscaling serverless.
- Pesquisa/indústria: otimizadores de plano guiados por ML, ajuste automático de parâmetros (ex.: OtterTune e sucessores), detecção de anomalias de workload.
Leitura realista: a IA automatiza o tuning rotineiro e a detecção de problemas, mas decisões de arquitetura (particionar? réplica? isolar workload? custo?) e incidentes complexos continuam humanos — agora com ferramentas melhores.
10.4 IA na operação: o "DBA copiloto"
- Diagnóstico assistido: colar um plano de execução no LLM e discutir hipóteses; agentes que leem métricas e sugerem causa-raiz.
- Geração de migrations, scripts de manutenção e runbooks — sempre com revisão e testes.
- Documentação automática de schemas e linhagem de dados.
- Dados sintéticos para ambientes de teste em conformidade com LGPD.
10.5 O que muda na carreira — resumo honesto
| Perde valor | Ganha valor |
|---|---|
| Escrever SQL simples "no braço" | Revisar/validar SQL gerado; conhecer o negócio por trás dos dados |
| Tarefas repetitivas de operação (backup manual, patch) | Arquitetura, confiabilidade (SRE de dados), custo, segurança |
| Ser "o único que conhece o schema" | Construir metadados, camada semântica e governança que humanos e IAs consomem |
| Decorar sintaxe de cada dialeto | Fundamentos profundos: modelagem, transações, planos de execução, distribuição |
| — | Novas fronteiras: vetores/RAG, plataformas de IA, engenharia de contexto sobre dados corporativos |
A IA não elimina quem trabalha com dados — multiplica quem tem fundamentos e expõe quem só decorava sintaxe. O banco de dados, aliás, ficou mais central: é ele que guarda a memória, o contexto e os vetores que alimentam a IA. Quem domina SQL + arquitetura + IA aplicada está no centro da década.
Carreiras, salários e certificações
Mercado de trabalho11.1 As três trilhas (e o que cada uma cobra)
| Trilha | Foco | Habilidades-chave | Capítulos desta apostila |
|---|---|---|---|
| DBA / Engenheiro de confiabilidade de dados | Disponibilidade, backup, performance, segurança | Administração, replicação, tuning, cloud, IaC, Linux, observabilidade | 6, 7, 8, 9 |
| Engenharia / Análise de dados | Pipelines, modelagem analítica, BI | SQL avançado (windows!), star schema, dbt, Python, Spark, warehouse cloud | 3, 4, 5, 9, 10 |
| Backend / Full-stack | Aplicações que usam o banco bem | Modelagem OLTP, transações, índices, ORMs sem armadilhas, migrations | 1–4, 7, 8 |
11.2 Faixas salariais no Brasil (referência CLT, ordem de grandeza)
| Nível | DBA | Eng./Analista de dados | Backend |
|---|---|---|---|
| Júnior | R$ 3,5–6 mil | R$ 4–7 mil | R$ 4–7 mil |
| Pleno | R$ 7–12 mil | R$ 8–14 mil | R$ 8–14 mil |
| Sênior | R$ 12–20 mil | R$ 14–22 mil | R$ 14–22 mil |
| Especialista / Staff | R$ 18–30 mil+ | R$ 20–35 mil+ | R$ 20–35 mil+ |
Valores variam bastante por região, setor (fintech paga mais) e modalidade — vagas remotas para o exterior em USD podem multiplicar essas faixas. Use como bússola, não como tabela oficial; confira pesquisas salariais atuais.
11.3 Certificações que o mercado reconhece
| Certificação | Área | Comentário |
|---|---|---|
| Microsoft DP-300 (Azure Database Administrator) | DBA SQL Server/Azure | A principal para DBA no mundo Microsoft |
| Microsoft DP-900 / DP-203→DP-700 (Fabric) | Fundamentos / Eng. de dados | DP-900 é ótima porta de entrada |
| AWS Certified Data Engineer – Associate | Dados na AWS | Substituiu a antiga Database – Specialty como alvo popular |
| Google Professional Data Engineer / Cloud SQL | Dados no GCP | Forte em empresas data-driven |
| Oracle OCA/OCP Database e MySQL 8.0 (1Z0-908) | DBA Oracle/MySQL | Exigidas em grandes corporações e consultorias |
| CertiProf/EDB PostgreSQL, EnterpriseDB Associate/Professional | DBA PostgreSQL | PG não tem certificação "oficial" única; experiência + projetos pesam mais |
| Snowflake SnowPro, Databricks Data Engineer | Plataformas analíticas | Muito citadas em vagas de dados |
11.4 Portfólio que convence recrutador
- GitHub com os 3 projetos do capítulo 14 documentados (README com diagrama, decisões e resultados de performance).
- Perfil no LinkedIn descrevendo problemas resolvidos com números ("reduzi query de 40s para 300ms criando índice parcial").
- Prática contínua: LeetCode/StrataScratch/HackerRank (trilhas de SQL), bases públicas (IMDB, dados abertos gov.br).
- Laboratório pessoal: Docker Compose com PG + MySQL + monitoramento — cite na entrevista.
Exercícios com gabarito
Básico → muito avançadoUse o schema dos capítulos 2–3 (clientes, pedidos, funcionarios). Tente resolver antes de abrir o gabarito.
1. Liste nome e e-mail dos clientes de Recife, em ordem alfabética.básico
SELECT nome, email
FROM clientes
WHERE cidade = 'Recife'
ORDER BY nome;
2. Quantos pedidos estão com status 'pendente' e qual o valor total deles?básico
SELECT COUNT(*) AS qtd, SUM(valor) AS total
FROM pedidos
WHERE status = 'pendente';
3. Mostre os pedidos de 2025 com valor entre R$ 100 e R$ 500, do maior para o menor.básico
SELECT *
FROM pedidos
WHERE data_pedido BETWEEN DATE '2025-01-01' AND DATE '2025-12-31'
AND valor BETWEEN 100 AND 500
ORDER BY valor DESC;
4. Total gasto por cidade, exibindo apenas cidades com mais de R$ 10.000 acumulados.intermediário
SELECT c.cidade, SUM(p.valor) AS total
FROM pedidos p
JOIN clientes c ON c.id = p.id_cliente
GROUP BY c.cidade
HAVING SUM(p.valor) > 10000
ORDER BY total DESC;
5. Clientes que nunca fizeram pedido (duas soluções).intermediário
-- Solução 1: anti-join com LEFT JOIN
SELECT c.*
FROM clientes c
LEFT JOIN pedidos p ON p.id_cliente = c.id
WHERE p.id IS NULL;
-- Solução 2: NOT EXISTS (preferível — imune a NULLs)
SELECT c.*
FROM clientes c
WHERE NOT EXISTS (SELECT 1 FROM pedidos p WHERE p.id_cliente = c.id);
6. Encontre e-mails duplicados na tabela de leads (email, id).intermediário
SELECT email, COUNT(*) AS repeticoes
FROM leads
GROUP BY email
HAVING COUNT(*) > 1;
Bônus (remoção mantendo o menor id, PG/MS):
DELETE FROM leads
WHERE id NOT IN (SELECT MIN(id) FROM leads GROUP BY email);
7. O 2º maior valor de pedido — sem usar LIMIT/TOP.intermediário
SELECT MAX(valor) AS segundo_maior
FROM pedidos
WHERE valor < (SELECT MAX(valor) FROM pedidos);
-- Alternativa geral (N-ésimo): DENSE_RANK() em subquery
8. Para cada cliente, o pedido mais recente (todas as colunas do pedido).avançado
SELECT id, id_cliente, valor, status, data_pedido FROM (
SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY id_cliente
ORDER BY data_pedido DESC, id DESC) AS rn
FROM pedidos p
) t
WHERE rn = 1;
Desempate por id DESC garante determinismo — detalhe que impressiona em entrevista.
9. Receita mensal de 2025 com variação percentual sobre o mês anterior.avançado
WITH mensal AS (
SELECT DATE_TRUNC('month', data_pedido) AS mes, -- MySQL: DATE_FORMAT(data_pedido,'%Y-%m-01')
SUM(valor) AS receita
FROM pedidos
WHERE data_pedido >= DATE '2025-01-01' AND data_pedido < DATE '2026-01-01'
GROUP BY 1
)
SELECT mes, receita,
LAG(receita) OVER (ORDER BY mes) AS receita_anterior,
ROUND(100.0 * (receita - LAG(receita) OVER (ORDER BY mes))
/ NULLIF(LAG(receita) OVER (ORDER BY mes), 0), 2) AS variacao_pct
FROM mensal
ORDER BY mes;
NULLIF(..., 0) evita divisão por zero — outro detalhe de sênior.
10. Clientes que compraram em pelo menos 3 meses distintos de 2025.avançado
SELECT id_cliente
FROM pedidos
WHERE data_pedido >= DATE '2025-01-01' AND data_pedido < DATE '2026-01-01'
GROUP BY id_cliente
HAVING COUNT(DISTINCT EXTRACT(MONTH FROM data_pedido)) >= 3;
11. Hierarquia: liste cada funcionário com o "caminho" até o CEO (ex.: CEO > Diretora > Ana).muito avançado
WITH RECURSIVE h AS (
SELECT id, nome, id_gerente, nome::TEXT AS caminho -- MS: CAST(nome AS VARCHAR(MAX))
FROM funcionarios WHERE id_gerente IS NULL
UNION ALL
SELECT f.id, f.nome, f.id_gerente, h.caminho || ' > ' || f.nome
FROM funcionarios f JOIN h ON f.id_gerente = h.id
)
SELECT nome, caminho FROM h;
12. "Gaps and islands": detectar sequências de dias consecutivos de login por usuário.muito avançado
-- Truque clássico: data - ROW_NUMBER() é constante dentro de uma sequência
WITH base AS (
SELECT id_usuario, data_login,
data_login - ROW_NUMBER() OVER (PARTITION BY id_usuario
ORDER BY data_login)::INT AS grupo
FROM (SELECT DISTINCT id_usuario, data_login FROM logins) d
)
SELECT id_usuario,
MIN(data_login) AS inicio,
MAX(data_login) AS fim,
COUNT(*) AS dias_consecutivos
FROM base
GROUP BY id_usuario, grupo
ORDER BY id_usuario, inicio;
Padrão "gaps and islands" — presença quase garantida em testes para vagas de dados sênior.
13. A query abaixo está lenta numa tabela de 50 milhões de pedidos. Diagnostique e corrija: SELECT * FROM pedidos WHERE YEAR(data_pedido) = 2025 AND status = 'pago';muito avançado
Problemas: (1) YEAR(data_pedido) aplica função à coluna → não-sargable, impede uso de índice; (2) SELECT * traz colunas desnecessárias; (3) provavelmente não há índice adequado.
-- Reescrita sargable
SELECT id, id_cliente, valor, data_pedido
FROM pedidos
WHERE data_pedido >= DATE '2025-01-01'
AND data_pedido < DATE '2026-01-01'
AND status = 'pago';
-- Índice composto: coluna de igualdade primeiro, intervalo depois
CREATE INDEX idx_pedidos_status_data ON pedidos (status, data_pedido);
-- Valide com EXPLAIN (ANALYZE): Seq Scan → Index Scan/Seek
14. Escreva a consulta que devolve os 5 documentos mais similares a um vetor de pergunta usando pgvector, filtrando por tenant.muito avançado · IA
SELECT id, conteudo,
embedding <=> :vetor_pergunta AS distancia
FROM documentos
WHERE tenant_id = :tenant -- segurança multi-tenant antes de tudo
ORDER BY embedding <=> :vetor_pergunta
LIMIT 5;
Em produção: índice HNSW, busca híbrida (combinar com full-text) e atenção ao filtro + índice aproximado (pré-filtrar pode exigir hnsw.iterative_scan ou particionamento por tenant).
Simulado de entrevista
Perguntas reais + respostas-modeloResponda em voz alta antes de abrir. As respostas-modelo mostram como estruturar, não para decorar.
“Qual a diferença entre WHERE e HAVING?”conceito
Resposta-modelo: WHERE filtra linhas antes da agregação; HAVING filtra grupos depois, por isso só HAVING pode usar funções agregadas. Exemplo: "vendas de 2025 (WHERE) agrupadas por cliente, mostrando só quem somou mais de 10 mil (HAVING)". Bônus: filtrar no WHERE sempre que possível é mais eficiente, pois reduz linhas antes do GROUP BY.
“Explique os tipos de JOIN e quando usar cada um.”conceito
INNER traz só correspondências; LEFT preserva a tabela da esquerda (útil para "todos os X, mesmo sem Y" e para anti-joins com IS NULL); FULL para conciliação de duas fontes; CROSS para combinações. Cite a armadilha: filtro da tabela direita no WHERE anula o LEFT — deve ir no ON. Dar um exemplo de negócio real vale mais que a definição.
“O que é um índice e quais as desvantagens de criar muitos?”performance
Estrutura (geralmente B-tree) que acelera buscas evitando varredura completa. Custos: espaço em disco, escrita mais lenta (todo INSERT/UPDATE/DELETE mantém cada índice) e risco de o otimizador escolher mal. Estratégia: indexar com base nas queries reais (WHERE/JOIN/ORDER BY), medir com EXPLAIN, remover índices não usados (pg_stat_user_indexes, sys.dm_db_index_usage_stats).
“Uma query que era rápida ficou lenta em produção. Como você investiga?”cenário DBA
Estrutura de resposta (mostra senioridade): 1) Confirmar sintoma e impacto (desde quando? só essa query?); 2) Comparar plano atual × histórico (Query Store no MS, pg_stat_statements + auto_explain no PG); 3) Hipóteses comuns: estatísticas desatualizadas → ANALYZE; crescimento de dados que mudou o plano; parameter sniffing (MS); bloat/vacuum atrasado (PG); lock/bloqueio concorrente; mudança de código/ORM; 4) Corrigir a causa, não o sintoma; 5) Prevenir: alertas de regressão de plano. Mencionar "primeiro medir, depois mexer" conta muitos pontos.
“Explique ACID e o que acontece se o servidor cair no meio de uma transação.”conceito
Definir as 4 propriedades e então: alterações vão primeiro para o log de transações (WAL/redo); no crash, o recovery reexecuta o que foi commitado (redo) e desfaz o que não foi (undo/descartado). Transação sem COMMIT desaparece — atomicidade preservada. Citar o nome do log no SGBD da vaga demonstra profundidade.
“Como você faria o deploy de uma alteração de schema em uma tabela de 200 milhões de linhas sem derrubar o sistema?”cenário sênior
Pontos esperados: evitar locks longos (no PG, ADD COLUMN com DEFAULT é barato desde a v11, mas ALTER TYPE reescreve a tabela); usar CREATE INDEX CONCURRENTLY (PG) / ONLINE = ON (MS) / algoritmos INPLACE ou gh-ost/pt-online-schema-change (MySQL); padrão expand–migrate–contract: adicionar coluna nova → backfill em lotes → trocar leitura/escrita → remover a antiga; sempre via migration versionada (Flyway/Liquibase), testada em staging com volume realista, com plano de rollback.
“SQL ou NoSQL para o nosso novo serviço?”arquitetura
Responder com perguntas: forma dos dados e relações? consistência exigida? padrão de acesso e escala? equipe conhece o quê? Regra prática defensável: comece com PostgreSQL (resolve 90% dos casos, incluindo JSON e vetores) e adote NoSQL para necessidades específicas comprovadas (cache → Redis; busca → OpenSearch; escrita massiva distribuída → Cassandra). Citar "persistência poliglota" e o custo operacional de cada tecnologia extra.
“Como a IA muda o seu trabalho com bancos de dados?”atualidade
Resposta equilibrada: uso IA como acelerador (gerar SQL/scripts de rotina, revisar planos, documentar) mantendo revisão humana; o SQL básico se comoditizou, então invisto em fundamentos (modelagem, transações, tuning) e nas novas demandas — busca vetorial/RAG, metadados e camada semântica para dados "IA-consultáveis", governança e segurança de acesso por agentes (read-only, RLS, limites). Fechar com exemplo próprio (ex.: pgvector no projeto do cap. 14) diferencia de quem só repete manchetes.
“O que você faria nos primeiros 30 dias como nosso DBA?”comportamental técnica
1) Inventário: instâncias, versões, tamanhos, donos; 2) Backups: verificar e TESTAR restauração (prioridade zero); 3) Segurança: superusuários, senhas, TLS, exposição de rede; 4) Monitoramento e alertas mínimos; 5) Top queries lentas e quick wins; 6) Documentar e propor roadmap. Mostra método e foco em risco antes de "sair otimizando".
Projetos práticos guiados (portfólio)
Do zero ao GitHubProjeto 1 — E-commerce OLTP completo todas as trilhas
- Suba o ambiente com Docker:
docker run -d -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:17(repita commysql:8.4emcr.microsoft.com/mssql/server:2022-latestpara comparar dialetos). - Modele: clientes, endereços, produtos, categorias, pedidos, pedido_itens, pagamentos — diagrama ER + DDL com PKs, FKs, CHECKs e UNIQUEs (caps. 3 e 5).
- Popule com 1 milhão de linhas geradas (
generate_seriesno PG ou script Python/Faker). - Escreva 15 consultas de negócio: top clientes, ticket médio mensal, produtos sem venda, curva ABC (window + NTILE), retenção por coorte.
- Otimize: capture o EXPLAIN de 3 queries lentas, crie índices, documente o antes/depois no README (esse "antes/depois" é o ouro do portfólio).
Projeto 2 — Operação de DBA: backup, desastre e réplica foco DBA
- Configure PITR:
pg_basebackup+ arquivamento de WAL; simule umDROP TABLEacidental e restaure para 1 minuto antes — grave o passo a passo (runbook). - Monte réplica streaming em segundo contêiner; meça o lag; promova a réplica (failover manual) e documente.
- Adicione monitoramento: postgres_exporter + Prometheus + Grafana com painéis de conexões, cache hit ratio e locks.
- Versione tudo (docker-compose + scripts) — isso demonstra o "DBA como código" do cap. 6.6.
Projeto 3 — Busca semântica com RAG sobre seus dados futuro
- No PG do Projeto 1, instale pgvector e crie a tabela
documentos(cap. 10.2) com descrições dos produtos. - Gere embeddings via API de um provedor (OpenAI/Voyage/etc.) ou modelo local (sentence-transformers) e carregue-os.
- Implemente busca híbrida: combine similaridade vetorial com full-text (
tsvector) e compare resultados. - Exponha um endpoint simples (FastAPI) que recebe a pergunta, busca os top-5 e monta o contexto para um LLM responder — um RAG mínimo e honesto.
- No README, discuta recall × latência do índice HNSW e as decisões de segurança (usuário read-only, RLS por tenant).
Cada projeto = 1 repositório com README contendo: problema, diagrama, decisões (e alternativas descartadas), números de performance e o que você faria diferente. Recrutadores técnicos leem READMEs, não 5.000 linhas de SQL.
Trilha de estudos sugerida (12 semanas)
| Semanas | Conteúdo | Entrega |
|---|---|---|
| 1–2 | Caps. 1–2 + instalação via Docker | 50 SELECTs em base de exemplo |
| 3–4 | Cap. 3 (JOINs, agregações, DDL) | Modelagem do Projeto 1 |
| 5–6 | Caps. 4–5 (windows, CTEs, normalização) | 15 consultas de negócio + exercícios 8–12 |
| 7–8 | Caps. 6–7 (administração e tuning) | Projeto 2 completo |
| 9 | Cap. 8 (segurança/LGPD) | Hardening dos projetos |
| 10 | Caps. 9–10 (moderno + IA) | Projeto 3 (RAG) |
| 11 | Cap. 11 + escolha de certificação | LinkedIn/GitHub polidos |
| 12 | Caps. 12–13 em ritmo de prova | Simulados cronometrados |