Visão geral: DDL, DML e DQL
Em SQL, é comum separar os comandos por finalidade: DDL (Data Definition Language) define estruturas (tabelas, colunas, restrições), DML (Data Manipulation Language) manipula dados (inserir, atualizar, excluir) e DQL (Data Query Language) consulta dados (SELECT). Em prova, a banca costuma misturar sintaxe com interpretação de resultados, principalmente em filtros, agregações, JOINs, NULL e transações.
Bloco prático 1: DDL (CREATE, ALTER, DROP)
1) CREATE TABLE: criando estruturas e restrições
O CREATE TABLE cria uma tabela e pode definir restrições de integridade. As mais cobradas: PRIMARY KEY (identificador único), UNIQUE (unicidade), NOT NULL (obrigatoriedade), CHECK (regra), FOREIGN KEY (referência a outra tabela).
Exemplo prático (tabelas típicas de domínio bancário):
CREATE TABLE cliente ( id_cliente INTEGER PRIMARY KEY, nome VARCHAR(100) NOT NULL, cpf CHAR(11) UNIQUE NOT NULL, dt_cadastro DATE NOT NULL);CREATE TABLE conta ( id_conta INTEGER PRIMARY KEY, id_cliente INTEGER NOT NULL, agencia CHAR(4) NOT NULL, numero CHAR(8) NOT NULL, saldo DECIMAL(15,2) NOT NULL DEFAULT 0, status CHAR(1) NOT NULL CHECK (status IN ('A','B')), CONSTRAINT fk_conta_cliente FOREIGN KEY (id_cliente) REFERENCES cliente(id_cliente));CREATE TABLE lancamento ( id_lanc INTEGER PRIMARY KEY, id_conta INTEGER NOT NULL, dt_lanc DATE NOT NULL, tipo CHAR(1) NOT NULL CHECK (tipo IN ('C','D')), valor DECIMAL(15,2) NOT NULL CHECK (valor > 0), descricao VARCHAR(200), CONSTRAINT fk_lanc_conta FOREIGN KEY (id_conta) REFERENCES conta(id_conta));Pontos de prova:
- DEFAULT é aplicado quando o valor não é informado no INSERT.
- CHECK valida domínio (ex.: status A/B).
- FOREIGN KEY impede inserir filho sem pai (salvo regras específicas do SGBD).
2) ALTER TABLE: evoluindo o esquema
O ALTER TABLE altera uma tabela existente: adicionar coluna, alterar tipo (quando permitido), adicionar/remover restrições.
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
Passo a passo prático:
- Adicionar coluna:
ALTER TABLE cliente ADD email VARCHAR(120);- Adicionar restrição (ex.: email único):
ALTER TABLE cliente ADD CONSTRAINT uq_cliente_email UNIQUE (email);- Remover restrição (nome da constraint precisa ser conhecido):
ALTER TABLE cliente DROP CONSTRAINT uq_cliente_email;Armadilha comum: em muitos SGBDs, alterar tipo/tamanho pode falhar se houver dados incompatíveis.
3) DROP: removendo objetos
DROP TABLE remove a tabela e seus dados. Em cenários com dependências (FK), pode falhar se houver tabelas referenciando, a menos que se use opções específicas do SGBD (não dependa disso em prova, a menos que esteja explícito).
DROP TABLE lancamento;Bloco prático 2: DML (INSERT, UPDATE, DELETE)
1) INSERT: inserindo linhas
O INSERT adiciona registros. Em prova, atenção a: ordem das colunas, valores compatíveis, campos NOT NULL e DEFAULT.
INSERT INTO cliente (id_cliente, nome, cpf, dt_cadastro, email)VALUES (1, 'Ana Silva', '12345678901', DATE '2025-01-10', 'ana@exemplo.com');INSERT INTO conta (id_conta, id_cliente, agencia, numero, saldo, status)VALUES (10, 1, '0001', '00001234', 1500.00, 'A');INSERT com coluna omitida: se a coluna tiver DEFAULT ou aceitar NULL, a inserção pode ocorrer.
INSERT INTO conta (id_conta, id_cliente, agencia, numero, status)VALUES (11, 1, '0001', '00005678', 'A');2) UPDATE: atualizando com segurança
O UPDATE altera linhas existentes. O ponto mais cobrado é o WHERE: sem WHERE, atualiza todas as linhas.
Passo a passo prático:
- Atualizar saldo de uma conta específica:
UPDATE contaSET saldo = saldo + 200.00WHERE id_conta = 10;- Atualizar com múltiplas condições:
UPDATE contaSET status = 'B'WHERE id_cliente = 1 AND saldo < 0;Tratamento de NULL no UPDATE: para preencher email ausente:
UPDATE clienteSET email = 'sem_email@exemplo.com'WHERE email IS NULL;3) DELETE: removendo linhas
O DELETE remove linhas. Assim como no UPDATE, o WHERE é crítico.
DELETE FROM lancamentoWHERE dt_lanc < DATE '2024-01-01';Armadilha comum: WHERE campo = NULL não funciona como esperado; use IS NULL.
Bloco prático 3: DQL (SELECT) e leitura de resultados
1) SELECT básico: projeção e FROM
O SELECT define quais colunas (projeção) e de quais tabelas (FROM). Em prova, avalie a ordem lógica de execução (conceitual): FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
SELECT id_cliente, nome, emailFROM cliente;2) WHERE: filtros, operadores e precedência
O WHERE filtra linhas antes de agregações. Operadores comuns: =, <>, >, <, BETWEEN, LIKE, IN, AND, OR, NOT.
Exemplos práticos:
SELECT *FROM contaWHERE status = 'A' AND saldo >= 1000;SELECT *FROM clienteWHERE nome LIKE 'Ana%';SELECT *FROM lancamentoWHERE dt_lanc BETWEEN DATE '2025-01-01' AND DATE '2025-01-31';Precedência: AND costuma ser avaliado antes de OR. Use parênteses para deixar explícito.
SELECT *FROM contaWHERE (status = 'A' OR status = 'B') AND saldo > 0;3) ORDER BY: ordenação
ORDER BY ordena o resultado final. Pode ordenar por coluna, alias ou posição (esta última é menos recomendada).
SELECT id_conta, saldoFROM contaWHERE status = 'A'ORDER BY saldo DESC, id_conta ASC;4) Agregações: COUNT, SUM, AVG (e cuidados com NULL)
Funções de agregação resumem várias linhas em um valor. Em geral, NULL é ignorado por SUM/AVG/COUNT(coluna). Já COUNT(*) conta linhas, incluindo as que têm NULL em colunas.
SELECT COUNT(*) AS total_contasFROM conta;SELECT COUNT(email) AS clientes_com_emailFROM cliente;SELECT SUM(saldo) AS soma_saldos, AVG(saldo) AS media_saldosFROM contaWHERE status = 'A';Armadilha típica: COUNT(email) não conta clientes com email NULL.
5) GROUP BY: agregando por grupos
GROUP BY agrupa linhas para calcular agregações por categoria. Regra de prova: no SELECT, toda coluna não agregada deve estar no GROUP BY.
SELECT id_cliente, COUNT(*) AS qtd_contas, SUM(saldo) AS total_saldoFROM contaGROUP BY id_cliente;6) HAVING: filtro após agregação
HAVING filtra grupos (após GROUP BY). Use WHERE para filtrar linhas antes de agrupar; HAVING para filtrar o resultado agregado.
SELECT id_cliente, SUM(saldo) AS total_saldoFROM contaGROUP BY id_clienteHAVING SUM(saldo) > 5000;Comparação rápida:
- WHERE: filtra linhas (antes do agrupamento).
- HAVING: filtra grupos (depois do agrupamento).
7) Tratamento de NULL: IS NULL, COALESCE e lógica de três valores
Em SQL, comparações com NULL não resultam em verdadeiro/falso, mas em desconhecido. Por isso, use IS NULL e IS NOT NULL.
SELECT *FROM clienteWHERE email IS NULL;COALESCE substitui NULL pelo primeiro valor não nulo, útil em relatórios.
SELECT nome, COALESCE(email, 'NÃO INFORMADO') AS email_exibicaoFROM cliente;JOINs: combinando tabelas
1) INNER JOIN: apenas correspondências
INNER JOIN retorna linhas que têm correspondência nas duas tabelas (pela condição ON).
SELECT c.nome, ct.agencia, ct.numero, ct.saldoFROM cliente cINNER JOIN conta ct ON ct.id_cliente = c.id_clienteWHERE ct.status = 'A';2) LEFT JOIN: mantém tudo da esquerda
LEFT JOIN retorna todas as linhas da tabela da esquerda, mesmo sem correspondência; colunas da direita vêm como NULL quando não há match. Muito cobrado para achar “sem relacionamento”.
SELECT c.id_cliente, c.nome, ct.id_contaFROM cliente cLEFT JOIN conta ct ON ct.id_cliente = c.id_clienteWHERE ct.id_conta IS NULL;Esse padrão encontra clientes sem conta.
3) RIGHT JOIN: mantém tudo da direita
RIGHT JOIN é o espelho do LEFT JOIN (mantém tudo da direita). Em prova, pode aparecer, mas muitas vezes pode ser reescrito invertendo as tabelas e usando LEFT JOIN.
SELECT c.nome, ct.id_contaFROM cliente cRIGHT JOIN conta ct ON ct.id_cliente = c.id_cliente;4) Cuidados com filtros em JOINs
Em LEFT JOIN, colocar filtro da tabela da direita no WHERE pode “transformar” o resultado em algo parecido com INNER JOIN (porque elimina as linhas com NULL). Alternativa: mover o filtro para o ON quando a intenção é preservar a tabela da esquerda.
SELECT c.nome, ct.id_contaFROM cliente cLEFT JOIN conta ct ON ct.id_cliente = c.id_cliente AND ct.status = 'A';Subconsultas, IN, EXISTS
1) Subconsulta com IN
IN testa pertencimento a uma lista (ou resultado de subconsulta). É comum para filtrar por conjunto de chaves.
SELECT nomeFROM clienteWHERE id_cliente IN ( SELECT id_cliente FROM conta WHERE saldo > 1000);Observação de prova: se a subconsulta retornar NULL, o comportamento pode afetar comparações em alguns cenários; em geral, prefira EXISTS quando a intenção é “existe relacionamento”.
2) Subconsulta correlacionada com EXISTS
EXISTS verifica se a subconsulta retorna ao menos uma linha. É muito usado para “clientes que possuem ao menos uma conta” ou “contas que possuem lançamentos”.
SELECT c.id_cliente, c.nomeFROM cliente cWHERE EXISTS ( SELECT 1 FROM conta ct WHERE ct.id_cliente = c.id_cliente);3) NOT EXISTS: ausência de relacionamento
SELECT c.id_cliente, c.nomeFROM cliente cWHERE NOT EXISTS ( SELECT 1 FROM conta ct WHERE ct.id_cliente = c.id_cliente);Operações de conjunto (quando pertinentes)
Operações de conjunto combinam resultados de SELECTs compatíveis (mesmo número e tipos de colunas). As mais comuns:
- UNION: une e remove duplicatas.
- UNION ALL: une e mantém duplicatas (geralmente mais eficiente).
- INTERSECT e EXCEPT/MINUS: podem existir conforme o padrão/implementação; em prova, foque no conceito.
SELECT id_cliente FROM conta WHERE status = 'A'UNIONSELECT id_cliente FROM conta WHERE saldo > 1000;Interpretação: clientes que têm conta ativa ou saldo acima de 1000 (sem duplicar ids).
Transações, ACID e bloqueios (conceitual)
1) O que é transação e por que importa
Transação é um conjunto de operações SQL que deve ser tratado como uma unidade lógica de trabalho. Em ambiente bancário, isso é essencial para evitar inconsistências (ex.: debitar de uma conta e creditar em outra).
2) COMMIT e ROLLBACK
- COMMIT: confirma as alterações feitas na transação.
- ROLLBACK: desfaz alterações não confirmadas.
UPDATE conta SET saldo = saldo - 100.00 WHERE id_conta = 10;UPDATE conta SET saldo = saldo + 100.00 WHERE id_conta = 11;COMMIT;Se ocorrer erro entre as operações, o correto é desfazer para não ficar “meia transferência”:
UPDATE conta SET saldo = saldo - 100.00 WHERE id_conta = 10;-- ocorreu um erro antes do créditoROLLBACK;3) ACID (conceitos cobrados)
- Atomicidade: tudo ou nada (ou confirma tudo, ou desfaz tudo).
- Consistência: regras de integridade são preservadas (constraints, chaves, checks).
- Isolamento: transações concorrentes não devem interferir de forma indevida (evitar leituras sujas, etc.).
- Durabilidade: após COMMIT, a alteração persiste mesmo com falhas.
4) Bloqueios (locks) em nível conceitual
Para garantir isolamento, o SGBD usa bloqueios. Ideia central para prova:
- Leituras e escritas concorrentes podem gerar anomalias.
- Bloqueios podem impedir que duas transações alterem o mesmo dado ao mesmo tempo.
- Dependendo do nível de isolamento, o SGBD pode permitir ou evitar certos fenômenos (ex.: leitura suja, leitura não repetível, phantom).
Sem entrar em sintaxe específica, o que a banca costuma cobrar é reconhecer que concorrência exige controle e que COMMIT/ROLLBACK delimitam a efetivação das mudanças.
Bateria de consultas típicas de prova (com plano lógico)
Consulta 1: listar contas ativas com saldo acima de um valor, ordenando
SELECT id_conta, agencia, numero, saldoFROM contaWHERE status = 'A' AND saldo > 1000ORDER BY saldo DESC;Plano lógico: FROM (conta) → WHERE filtra status e saldo → SELECT projeta colunas → ORDER BY ordena por saldo desc.
Consulta 2: total de contas por cliente (com agregação)
SELECT id_cliente, COUNT(*) AS qtd_contasFROM contaGROUP BY id_cliente;Plano lógico: FROM → GROUP BY cria grupos por id_cliente → COUNT calcula por grupo → SELECT retorna uma linha por cliente.
Consulta 3: clientes com total de saldo acima de 5000 (HAVING)
SELECT c.id_cliente, c.nome, SUM(ct.saldo) AS total_saldoFROM cliente cINNER JOIN conta ct ON ct.id_cliente = c.id_clienteGROUP BY c.id_cliente, c.nomeHAVING SUM(ct.saldo) > 5000ORDER BY total_saldo DESC;Plano lógico: FROM + JOIN combina cliente/conta → GROUP BY agrupa por cliente → HAVING filtra grupos pelo SUM → SELECT projeta → ORDER BY ordena pelo total.
Consulta 4: clientes sem conta (LEFT JOIN + IS NULL)
SELECT c.id_cliente, c.nomeFROM cliente cLEFT JOIN conta ct ON ct.id_cliente = c.id_clienteWHERE ct.id_conta IS NULL;Plano lógico: FROM cliente → LEFT JOIN tenta casar conta → WHERE mantém apenas os casos sem correspondência (ct.id_conta NULL).
Consulta 5: contas que tiveram ao menos um lançamento de débito (EXISTS)
SELECT ct.id_conta, ct.agencia, ct.numeroFROM conta ctWHERE EXISTS ( SELECT 1 FROM lancamento l WHERE l.id_conta = ct.id_conta AND l.tipo = 'D');Plano lógico: FROM conta → WHERE EXISTS testa, para cada conta, se há ao menos um lançamento do tipo D.
Consulta 6: contas sem lançamentos (NOT EXISTS)
SELECT ct.id_contaFROM conta ctWHERE NOT EXISTS ( SELECT 1 FROM lancamento l WHERE l.id_conta = ct.id_conta);Plano lógico: FROM conta → WHERE NOT EXISTS mantém contas para as quais a subconsulta não retorna linhas.
Consulta 7: diferença entre COUNT(*) e COUNT(coluna)
SELECT COUNT(*) AS total_linhas, COUNT(descricao) AS descricoes_preenchidasFROM lancamento;Plano lógico: FROM lancamento → agregações calculadas sobre o conjunto inteiro; COUNT(descricao) ignora NULL em descricao.
Consulta 8: filtrar por lista (IN) e reforçar precedência
SELECT id_conta, status, saldoFROM contaWHERE status IN ('A','B') AND (saldo < 0 OR saldo > 10000);Plano lógico: FROM → WHERE aplica IN e parênteses para controlar AND/OR → SELECT projeta.
Consulta 9: unir conjuntos (UNION vs UNION ALL)
SELECT id_cliente FROM conta WHERE status = 'A'UNION ALLSELECT id_cliente FROM conta WHERE saldo > 1000;Plano lógico: executa os dois SELECTs separadamente e concatena resultados; com UNION ALL, duplicatas permanecem (um mesmo cliente pode aparecer duas vezes).
Consulta 10: relatório com NULL tratado (COALESCE) e ordenação
SELECT nome, COALESCE(email, 'NÃO INFORMADO') AS email_exibicaoFROM clienteORDER BY nome ASC;Plano lógico: FROM cliente → SELECT calcula coluna derivada com COALESCE → ORDER BY ordena por nome.