-- SELECT conhecimento FROM apostila;

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.

Multi-banco PostgreSQL MySQL / MariaDB SQL Server IA & futuro da profissão Atualizada · 2026
-- capítulo 01

Bancos de dados e o modelo relacional

Nível: básico

Um 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:

PropriedadeSignificadoExemplo prático
AtomicidadeTudo ou nada.Se o crédito falhar, o débito é desfeito (rollback).
ConsistênciaO banco vai de um estado válido para outro estado válido.Constraints e regras nunca ficam violadas ao final.
IsolamentoTransações concorrentes não se enxergam "no meio do caminho".Outro usuário não vê a conta com saldo intermediário.
DurabilidadeDepois 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

SGBDLicençaPontos fortesOnde aparece nas vagas
PostgreSQLOpen sourcePadrão SQL rigoroso, extensões (PostGIS, pgvector), JSONB, confiabilidadeStartups, fintechs, governo, dados/IA — o queridinho da década
MySQL / MariaDBOpen source (Oracle / comunidade)Simplicidade, replicação madura, enorme base instalada na webE-commerce, web, WordPress, empresas com stack LAMP
SQL ServerComercial (Microsoft)Integração com ecossistema Microsoft, SSMS, ferramentas de BI (SSIS/SSRS)Grandes empresas, indústria, varejo, órgãos públicos
Oracle DatabaseComercialRobustez extrema, RAC, recursos corporativosBancos, telecom, grandes ERPs
SQLiteDomínio públicoEmbutido, zero configuraçãoApps mobile, dispositivos, testes
Visão de mercado

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:

GrupoSiglaComandos
Definição de dadosDDLCREATE, ALTER, DROP, TRUNCATE
Manipulação de dadosDMLINSERT, UPDATE, DELETE, MERGE
Consulta de dadosDQLSELECT
Controle de acessoDCLGRANT, REVOKE
Controle de transaçãoTCLBEGIN/START TRANSACTION, COMMIT, ROLLBACK, SAVEPOINT

1.5 Tipos de dados essenciais

CategoriaPostgreSQLMySQLSQL Server
InteiroSMALLINT, INT, BIGINTiguais + TINYINTiguais + TINYINT
Decimal exato (dinheiro!)NUMERIC(12,2)DECIMAL(12,2)DECIMAL(12,2)
Ponto flutuanteREAL, DOUBLE PRECISIONFLOAT, DOUBLEFLOAT
TextoVARCHAR(n), TEXTVARCHAR(n), TEXTVARCHAR(n), NVARCHAR(n) (Unicode)
Data/horaDATE, TIMESTAMPTZDATE, DATETIME, TIMESTAMPDATE, DATETIME2, DATETIMEOFFSET
BooleanoBOOLEANTINYINT(1)/BOOLBIT
Auto-incrementoGENERATED ALWAYS AS IDENTITYAUTO_INCREMENTIDENTITY(1,1)
UUIDUUIDCHAR(36)/BINARY(16)UNIQUEIDENTIFIER
JSONJSONB (indexável!)JSONNVARCHAR(MAX) + funções JSON (tipo nativo JSON no SQL Server 2025)
Erro clássico que reprova em entrevista

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.


-- capítulo 02

SQL básico: consultas e manipulação

Nível: básico

Usaremos 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

OperadorExemploObservação
= <> > < >= <=valor >= 100<> é "diferente" (também !=)
BETWEENvalor BETWEEN 50 AND 100Inclusivo nas duas pontas
INstatus IN ('pago','enviado')Lista de valores
LIKEnome LIKE 'Ma%'% = qualquer sequência; _ = 1 caractere. PG: ILIKE ignora maiúsculas
IS NULL / IS NOT NULLcidade IS NULLNunca use = NULL — não funciona!
AND / OR / NOTa AND (b OR c)Use parênteses; AND tem precedência sobre OR
Entendendo NULL de uma vez por todas

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';
Ritual de sobrevivência profissional

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:

FROMJOIN/ONWHEREGROUP BYHAVINGSELECTDISTINCTORDER BYLIMIT

É por isso que você não pode usar um alias do SELECT dentro do WHERE (o WHERE executa antes) — mas pode no ORDER BY.


-- capítulo 03

SQL intermediário: JOINs, agregações e DDL

Nível: intermediário

3.1 JOINs — combinando tabelas

JOINs são o coração do SQL relacional e o assunto nº 1 em testes técnicos.

JOINO que retornaUso típico
INNER JOINSomente linhas com correspondência nas duas tabelasPedidos com seus clientes
LEFT JOINTodas as linhas da esquerda + correspondências da direita (NULL onde não há)Todos os clientes, tenham ou não pedidos
RIGHT JOINEspelho do LEFTRaro; prefira reescrever como LEFT
FULL OUTER JOINTudo dos dois ladosConciliações. MySQL não tem — simule com LEFT UNION RIGHT
CROSS JOINProduto cartesiano (todas as combinações)Gerar grades/combinações
Self joinTabela com ela mesmaFuncioná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;
Armadilha de entrevista

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_BY corrige).
  • WHERE não pode usar agregação; HAVING pode. 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.


-- capítulo 04

SQL avançado: window functions, CTEs e recursos modernos

Nível: avançado

4.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ívelEvitaAnomalias possíveis
READ UNCOMMITTEDLeitura suja, não repetível, fantasma
READ COMMITTED (padrão PG, MS, Oracle)Leitura sujaNão repetível, fantasma
REPEATABLE READ (padrão MySQL/InnoDB)+ leitura não repetívelFantasma (no InnoDB, quase eliminado por gap locks)
SERIALIZABLETodas— (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.


-- capítulo 05

Modelagem de dados e normalização

Nível: intermediário

5.1 Do requisito ao modelo

  1. Modelo conceitual — diagrama entidade-relacionamento (ER): entidades, atributos, cardinalidades (1:1, 1:N, N:N).
  2. Modelo lógico — tabelas, colunas, chaves; N:N vira tabela associativa (ex.: pedido_itens).
  3. Modelo físico — tipos concretos, índices, particionamento, específicos do SGBD.

5.2 Formas normais (o essencial que caem em prova e entrevista)

FormaRegraCheiro de violação
1FNValores atômicos; sem grupos repetidosColuna telefones = '9999, 8888'
2FN1FN + todo atributo depende da chave inteiraEm pedido_itens(pedido, produto, nome_produto), o nome depende só de produto
3FN2FN + sem dependência transitivaclientes(id, cidade, uf_da_cidade) — UF depende da cidade, não do id
BCNFToda dependência parte de uma chave candidataCasos 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)
);
O que as vagas pedem aqui

"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.


-- capítulo 06

Administração: o dia a dia do DBA

Nível: avançado

6.1 Arquitetura interna (o que todo DBA precisa visualizar)

  • Memória: cache de páginas de dados — PG shared_buffers, MY innodb_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

ConceitoSignificado
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ógicoExporta comandos/dados (pg_dump, mysqldump/mysqlpump, bacpac) — portátil, mais lento para restaurar
Backup físicoCopia 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';
Regra de ouro

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; ANALYZE atualiza estatísticas; monitore bloat.
  • MY: ANALYZE TABLE, OPTIMIZE TABLE ocasional, 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.


-- capítulo 07

Performance e tuning

Nível: avançado

7.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 por status ou por status+data, mas não por data_pedido sozinho.
  • 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)=2025 vira scan; escreva WHERE 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 planoLeitura
Seq Scan / Table Scan / Full ScanVarredura completa — ok em tabelas pequenas ou quando retorna grande fração das linhas; suspeito com WHERE seletivo
Index Scan / Index SeekUso do índice — o que você geralmente quer para buscas pontuais
Nested LoopBom para poucos registros externos com índice no interno
Hash JoinBom para volumes grandes sem ordenação
Merge JoinÓtimo quando as duas entradas já estão ordenadas
Sort / Filter caros, "rows" estimado ≠ realEstatísticas desatualizadas → ANALYZE/UPDATE STATISTICS

7.3 Metodologia de tuning (roteiro de entrevista sênior)

  1. Meça: identifique as queries mais custosas — pg_stat_statements, slow_query_log, Query Store.
  2. Explique: rode EXPLAIN ANALYZE; compare linhas estimadas × reais.
  3. 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.
  4. 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

ObjetivoPostgreSQLMySQLSQL Server
Cache de dadosshared_buffers (~25% RAM) + effective_cache_sizeinnodb_buffer_pool_size (~70% RAM dedicada)max server memory
Memória por operaçãowork_memsort_buffer_size etc.memory grants (automático)
Escrita/logwal_compression, max_wal_sizeinnodb_redo_log_capacity, innodb_flush_log_at_trx_commitposicionar log em disco rápido; VLFs saudáveis
Paralelismomax_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.


-- capítulo 08

Segurança e LGPD

Nível: avançado

8.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-full no PG; Encrypt=True no 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.
Diferencial de contratação

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.


-- capítulo 09

O ecossistema moderno: cloud, NoSQL, NewSQL e além

Nível: muito avançado

9.1 Bancos gerenciados na nuvem — onde o mercado está

NuvemServiços principaisO que saber
AWSRDS (PG/MySQL/MS/Oracle), Aurora (PG/MySQL com storage distribuído), Aurora Serverless, DynamoDB, RedshiftAurora separa computação de storage (6 cópias em 3 AZs); Serverless v2 escala por ACUs
AzureAzure SQL Database/Managed Instance, Database for PostgreSQL/MySQL (Flexible Server), Cosmos DB, Synapse/FabricForte onde já há ecossistema Microsoft; Hyperscale para bases enormes
Google CloudCloud SQL, AlloyDB (PG turbinado, colunar em memória), Spanner, BigQuerySpanner = SQL distribuído global com consistência forte
IndependentesNeon (PG serverless, branching de banco), Supabase (PG + APIs), PlanetScale (MySQL/Vitess), TiDB, CockroachDB, Snowflake, DatabricksBranching 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

TipoExemplosCaso de uso
DocumentoMongoDB, Couchbase, FirestoreEsquema flexível, catálogos, perfis
Chave-valorRedis, Valkey, DynamoDBCache, sessões, filas leves, latência de microssegundos
Colunar-wideCassandra, ScyllaDB, HBaseEscrita massiva distribuída, séries de eventos
GrafosNeo4j, NeptuneRelacionamentos profundos: fraude, redes, recomendação
Séries temporaisTimescaleDB (PG!), InfluxDB, ClickHouseMétricas, IoT, telemetria
BuscaElasticsearch/OpenSearchFull-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.

-- capítulo 10

Inteligência Artificial e bancos de dados: o que muda (e o que não muda)

Nível: muito avançado · visão de futuro

10.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 valorGanha 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 dialetoFundamentos profundos: modelagem, transações, planos de execução, distribuição
Novas fronteiras: vetores/RAG, plataformas de IA, engenharia de contexto sobre dados corporativos
Síntese

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.


-- capítulo 11

Carreiras, salários e certificações

Mercado de trabalho

11.1 As três trilhas (e o que cada uma cobra)

TrilhaFocoHabilidades-chaveCapítulos desta apostila
DBA / Engenheiro de confiabilidade de dadosDisponibilidade, backup, performance, segurançaAdministração, replicação, tuning, cloud, IaC, Linux, observabilidade6, 7, 8, 9
Engenharia / Análise de dadosPipelines, modelagem analítica, BISQL avançado (windows!), star schema, dbt, Python, Spark, warehouse cloud3, 4, 5, 9, 10
Backend / Full-stackAplicações que usam o banco bemModelagem OLTP, transações, índices, ORMs sem armadilhas, migrations1–4, 7, 8

11.2 Faixas salariais no Brasil (referência CLT, ordem de grandeza)

NívelDBAEng./Analista de dadosBackend
JúniorR$ 3,5–6 milR$ 4–7 milR$ 4–7 mil
PlenoR$ 7–12 milR$ 8–14 milR$ 8–14 mil
SêniorR$ 12–20 milR$ 14–22 milR$ 14–22 mil
Especialista / StaffR$ 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ÁreaComentário
Microsoft DP-300 (Azure Database Administrator)DBA SQL Server/AzureA principal para DBA no mundo Microsoft
Microsoft DP-900 / DP-203→DP-700 (Fabric)Fundamentos / Eng. de dadosDP-900 é ótima porta de entrada
AWS Certified Data Engineer – AssociateDados na AWSSubstituiu a antiga Database – Specialty como alvo popular
Google Professional Data Engineer / Cloud SQLDados no GCPForte em empresas data-driven
Oracle OCA/OCP Database e MySQL 8.0 (1Z0-908)DBA Oracle/MySQLExigidas em grandes corporações e consultorias
CertiProf/EDB PostgreSQL, EnterpriseDB Associate/ProfessionalDBA PostgreSQLPG não tem certificação "oficial" única; experiência + projetos pesam mais
Snowflake SnowPro, Databricks Data EngineerPlataformas analíticasMuito 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.

-- capítulo 12

Exercícios com gabarito

Básico → muito avançado

Use 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).


-- capítulo 13

Simulado de entrevista

Perguntas reais + respostas-modelo

Responda 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".


-- capítulo 14

Projetos práticos guiados (portfólio)

Do zero ao GitHub

Projeto 1 — E-commerce OLTP completo todas as trilhas

  1. Suba o ambiente com Docker: docker run -d -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:17 (repita com mysql:8.4 e mcr.microsoft.com/mssql/server:2022-latest para comparar dialetos).
  2. Modele: clientes, endereços, produtos, categorias, pedidos, pedido_itens, pagamentos — diagrama ER + DDL com PKs, FKs, CHECKs e UNIQUEs (caps. 3 e 5).
  3. Popule com 1 milhão de linhas geradas (generate_series no PG ou script Python/Faker).
  4. Escreva 15 consultas de negócio: top clientes, ticket médio mensal, produtos sem venda, curva ABC (window + NTILE), retenção por coorte.
  5. 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

  1. Configure PITR: pg_basebackup + arquivamento de WAL; simule um DROP TABLE acidental e restaure para 1 minuto antes — grave o passo a passo (runbook).
  2. Monte réplica streaming em segundo contêiner; meça o lag; promova a réplica (failover manual) e documente.
  3. Adicione monitoramento: postgres_exporter + Prometheus + Grafana com painéis de conexões, cache hit ratio e locks.
  4. 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

  1. No PG do Projeto 1, instale pgvector e crie a tabela documentos (cap. 10.2) com descrições dos produtos.
  2. Gere embeddings via API de um provedor (OpenAI/Voyage/etc.) ou modelo local (sentence-transformers) e carregue-os.
  3. Implemente busca híbrida: combine similaridade vetorial com full-text (tsvector) e compare resultados.
  4. 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.
  5. No README, discuta recall × latência do índice HNSW e as decisões de segurança (usuário read-only, RLS por tenant).
Como apresentar

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)

SemanasConteúdoEntrega
1–2Caps. 1–2 + instalação via Docker50 SELECTs em base de exemplo
3–4Cap. 3 (JOINs, agregações, DDL)Modelagem do Projeto 1
5–6Caps. 4–5 (windows, CTEs, normalização)15 consultas de negócio + exercícios 8–12
7–8Caps. 6–7 (administração e tuning)Projeto 2 completo
9Cap. 8 (segurança/LGPD)Hardening dos projetos
10Caps. 9–10 (moderno + IA)Projeto 3 (RAG)
11Cap. 11 + escolha de certificaçãoLinkedIn/GitHub polidos
12Caps. 12–13 em ritmo de provaSimulados cronometrados