timeline
title Evolucao da SQL
1970 : Modelo relacional de Codd
1974 : SEQUEL na IBM
1977 : Renomeada para SQL
1979 : Oracle comercial
1986 : Padrao ANSI SQL-86
1992 : SQL-92
1999 : SQL-1999
2003 : Window functions
SQL: fundamentos (DDL, DML, DQL, DCL, TCL)
Chegamos ao módulo em que a teoria que viemos construindo finalmente vira comando digitado e executado. Até aqui falamos de modelo relacional, de tabelas, de chaves e de relacionamentos como conceitos. Agora vou te apresentar a linguagem que materializa tudo isso dentro de um SGBD: a SQL. Quero que você termine esta leitura sabendo não só escrever um SELECT, mas entendendo por que a SQL é dividida em cinco sublinguagens, o que cada uma faz e onde elas se encaixam no dia a dia de quem administra ou consulta um banco. Esse assunto é campeão de questões em prova justamente porque permite cobrar tanto sintaxe seca quanto entendimento conceitual. Vou te mostrar os dois lados, sempre com PostgreSQL nos exemplos, que é o dialeto que você precisa dominar aqui.
Um aviso antes de começarmos: as consultas mais sofisticadas (junções entre tabelas, subconsultas, funções de janela e agregações) ficam reservadas para o módulo 10, e o estudo aprofundado de transações e isolamento vem no módulo 12. Aqui o objetivo é construir o alicerce: criar estruturas, povoá-las com dados, recuperá-las de forma simples, controlar quem acessa e agrupar operações em transações básicas.
Um pouco de história: de SEQUEL a SQL
Toda essa história começa em 1970, quando Edgar F. Codd, matemático da IBM, publicou o artigo que fundou o modelo relacional. Codd propôs organizar dados em tabelas bidimensionais apoiadas na teoria dos conjuntos e na lógica de predicados, rompendo com os modelos hierárquico e de rede que dominavam a época. A grande sacada foi separar a estrutura lógica dos dados de sua implementação física e apoiar tudo em uma base matemática sólida.
Inspirados nesse trabalho, Donald Chamberlin e Raymond Boyce, também na IBM, criaram em 1974 uma linguagem para concretizar as ideias de Codd: a SEQUEL, sigla de Structured English Query Language. A intenção era que ela fosse próxima do inglês, legível até por quem não era programador. Em 1977 o nome teve de mudar para SQL por uma questão de marca registrada, e a IBM passou a implementá-la no System R, um dos primeiros SGBDRs da história. Curiosamente, quem chegou primeiro ao mercado não foi a IBM: a Relational Software, que viria a se chamar Oracle, lançou seu produto comercial em 1979.
À medida que vários fornecedores criavam dialetos próprios, surgiu a necessidade de padronização. Em 1986 o ANSI publicou o SQL-86, seguido pelo padrão ISO em 1987. De lá para cá vieram revisões sucessivas, e vale guardar essa linha do tempo porque ela aparece em prova com frequência.
| Versão | Ano | Principais novidades |
|---|---|---|
| SQL-86 | 1986 | Primeiro padrão ANSI |
| SQL-89 | 1989 | Revisão menor, integridade referencial |
| SQL-92 (SQL2) | 1992 | Grande revisão, muitos recursos novos |
| SQL:1999 (SQL3) | 1999 | Orientação a objetos, gatilhos, recursão |
| SQL:2003 | 2003 | XML e funções de janela (window functions) |
| SQL:2008/2011/2016/2019 | — | Refinamentos sucessivos, JSON, temporal |
Por que a SQL sobreviveu a tudo. Mesmo com a ascensão de bancos NoSQL e de plataformas de big data, a SQL permanece central. Isso se deve à sua maturidade e confiabilidade após décadas de uso em produção, à existência de padrões que favorecem portabilidade, à expressividade que permite descrever consultas complexas de forma concisa e a uma enorme base de profissionais que a dominam. Hoje ela reaparece adaptada para big data (Hive, Presto), para o NewSQL (Spanner, CockroachDB) e até para processamento em streaming (Flink, Kafka Streams).
A SQL não é uma só: as cinco sublinguagens
Aqui está o conceito organizador deste módulo. A SQL costuma ser tratada como uma linguagem única, mas na prática ela é um conjunto de sublinguagens, cada uma com um propósito bem delimitado. Entender essa divisão é o que separa quem decora comandos de quem realmente compreende a linguagem. Quando uma prova pergunta “o comando TRUNCATE pertence a qual categoria?”, ela está testando exatamente esse mapa mental que vou te dar agora.
graph TD
SQL[SQL] --> DDL[DDL - Definicao]
SQL --> DML[DML - Manipulacao]
SQL --> DQL[DQL - Consulta]
SQL --> DCL[DCL - Controle de Acesso]
SQL --> TCL[TCL - Controle de Transacao]
DDL --> D1[CREATE ALTER DROP TRUNCATE]
DML --> D2[INSERT UPDATE DELETE]
DQL --> D3[SELECT]
DCL --> D4[GRANT REVOKE]
TCL --> D5[BEGIN COMMIT ROLLBACK SAVEPOINT]
| Sublinguagem | Propósito | Comandos típicos |
|---|---|---|
| DDL | Definir e alterar a estrutura | CREATE, ALTER, DROP, TRUNCATE |
| DML | Manipular os dados | INSERT, UPDATE, DELETE |
| DQL | Consultar os dados | SELECT |
| DCL | Controlar privilégios de acesso | GRANT, REVOKE |
| TCL | Gerenciar transações | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
Repare que muitos autores tratam DQL como parte da DML, já que o SELECT também opera sobre dados. Para fins de prova, vale conhecer as duas leituras: a divisão em cinco categorias é a mais didática e detalhada, mas você pode encontrar a versão em quatro, em que o SELECT é absorvido pela DML. Vou seguir a divisão em cinco porque ela deixa cada papel mais nítido.
DDL: definindo a estrutura
A DDL é onde desenhamos o esqueleto do banco. Antes de qualquer dado existir, precisamos declarar tabelas, colunas, tipos e restrições. Vou trabalhar com um cenário de universidade ao longo do módulo, porque é um domínio rico o bastante para mostrar quase tudo.
Tipos de dados no PostgreSQL
Cada coluna tem um tipo, e o tipo define o que pode ser armazenado e quanto espaço ocupa. O PostgreSQL é generoso em tipos; deixo abaixo os que você mais encontrará.
| Tipo | Uso | Observação |
|---|---|---|
INTEGER / INT |
inteiros de 32 bits | faixa de cerca de -2,1 bi a 2,1 bi |
BIGINT |
inteiros de 64 bits | para valores muito grandes |
SERIAL / BIGSERIAL |
inteiro autoincremento | atalho que cria uma sequência |
NUMERIC(p,s) |
decimal exato | ideal para dinheiro |
REAL / DOUBLE PRECISION |
ponto flutuante | sujeito a arredondamento |
VARCHAR(n) |
texto de tamanho variável | limite de n caracteres |
CHAR(n) |
texto de tamanho fixo | preenchido com espaços |
TEXT |
texto sem limite | muito usado no PostgreSQL |
DATE |
data | sem horário |
TIMESTAMP |
data e hora | há a variante WITH TIME ZONE |
BOOLEAN |
verdadeiro/falso | aceita TRUE, FALSE, NULL |
UUID |
identificador universal | chaves distribuídas |
JSONB |
JSON binário | indexável, semiestruturado |
Atenção a um erro frequente. Para valores monetários, nunca use REAL ou DOUBLE PRECISION. Esses tipos de ponto flutuante introduzem erros de arredondamento, e 0.1 + 0.2 pode não dar exatamente 0.3. Use NUMERIC(p,s), que armazena o valor de forma exata. Em prova, essa é uma pegadinha clássica.
CREATE: criando objetos
O CREATE TABLE é o comando que mais você vai escrever. Vou criar a tabela de cursos primeiro, porque ela será referenciada pelas demais.
Veja quanta informação está condensada aqui. SERIAL PRIMARY KEY cria uma coluna inteira que se autoincrementa e serve de chave primária. NOT NULL proíbe ausência de valor. UNIQUE impede nomes de curso repetidos. CHECK impõe uma regra de domínio (duração entre 1 e 12 semestres). E DEFAULT CURRENT_TIMESTAMP preenche automaticamente a data de criação. Restrições assim são parte da DDL e são o que garante a integridade dos dados desde o nascimento da tabela.
Agora a tabela de estudantes, que se conecta a cursos por uma chave estrangeira.
A linha curso_id INTEGER REFERENCES cursos(id) declara a chave estrangeira: cada estudante aponta para um curso existente, e o banco recusará um curso_id que não exista em cursos. Isso é a integridade referencial atuando, exatamente o conceito de relacionamento 1:N que vimos antes — um curso tem muitos estudantes, cada estudante pertence a um curso.
Vou deixar um diagrama do nosso pequeno esquema para você visualizar.
erDiagram
CURSOS ||--o{ ESTUDANTES : possui
CURSOS {
int id PK
varchar nome
int duracao
}
ESTUDANTES {
int id PK
varchar nome
int curso_id FK
numeric mensalidade
}
Quando você precisa de uma restrição com nome próprio (útil para depois alterá-la), pode declará-la explicitamente:
CREATE TABLE matriculas (
estudante_id INTEGER NOT NULL,
curso_id INTEGER NOT NULL,
semestre VARCHAR(6) NOT NULL,
CONSTRAINT pk_matriculas PRIMARY KEY (estudante_id, curso_id, semestre),
CONSTRAINT fk_mat_estudante FOREIGN KEY (estudante_id) REFERENCES estudantes(id),
CONSTRAINT fk_mat_curso FOREIGN KEY (curso_id) REFERENCES cursos(id)
);Essa tabela matriculas é a tabela de junção que implementa um relacionamento muitos-para-muitos: como um estudante cursa várias disciplinas ao longo de vários semestres, a chave primária é composta por três colunas. Guarde esse padrão, ele aparece sempre que houver um M:N.
ALTER: modificando o que já existe
A estrutura raramente nasce pronta. ALTER TABLE permite evoluí-la sem recriar nada.
Cada uma dessas linhas faz uma cirurgia precisa: adicionar coluna, tornar uma coluna obrigatória, renomear, adicionar restrição e remover coluna. O PostgreSQL executa boa parte dessas operações de forma transacional, ou seja, dentro de uma transação você pode até desfazê-las, o que é uma vantagem importante.
DROP e TRUNCATE: removendo
Há uma diferença que cai muito em prova, e quero que você nunca mais confunda. DROP remove o objeto inteiro — a tabela deixa de existir, estrutura e dados. TRUNCATE mantém a tabela viva, apenas esvazia todas as linhas de uma vez.
| Aspecto | DELETE |
TRUNCATE |
DROP |
|---|---|---|---|
| Categoria | DML | DDL | DDL |
| Remove dados | sim (linha a linha) | sim (todas de uma vez) | sim |
| Remove estrutura | não | não | sim |
Aceita WHERE |
sim | não | não |
Reversível por ROLLBACK |
sim | sim no PostgreSQL | sim no PostgreSQL |
| Velocidade em massa | lenta | muito rápida | imediata |
Cuidado com a categorização. O DELETE parece primo do TRUNCATE, mas pertence à DML, não à DDL. A diferença não é só de família: o DELETE percorre linha a linha respeitando o WHERE e gera log detalhado, enquanto o TRUNCATE realoca o armazenamento de forma global e por isso é muito mais rápido para esvaziar tabelas grandes — ao custo de não aceitar filtro.
DML: manipulando os dados
Com a estrutura pronta, é hora de povoá-la. A DML cuida das três operações de escrita: inserir, atualizar e apagar.
INSERT
O segundo INSERT mostra a inserção múltipla: várias linhas em uma única instrução, o que é mais eficiente do que repetir o comando. Repare que não informei id nem criado_em: o SERIAL e o DEFAULT cuidam disso. Agora os estudantes:
O PostgreSQL oferece um recurso elegante para recuperar valores gerados na própria inserção, a cláusula RETURNING:
UPDATE
O UPDATE modifica linhas existentes, e aqui mora um perigo que todo iniciante leva um susto ao descobrir.
O esquecimento mais caro da SQL. Um UPDATE ou DELETE sem cláusula WHERE afeta TODAS as linhas da tabela. Se você rodar UPDATE estudantes SET ativo = FALSE; sem filtro, desativa o banco inteiro. Antes de executar uma alteração em produção, ganhe o hábito de escrever primeiro o WHERE e só depois o resto. Esse é, de longe, o erro operacional mais comum e mais doloroso.
DELETE
Vale o mesmo aviso do UPDATE: sem WHERE, o DELETE apaga tudo. A diferença para o TRUNCATE, que já mostrei, é que o DELETE é seletivo e mais lento em massa.
DQL: consultando os dados
Chegamos ao SELECT, o comando mais usado e mais versátil de toda a SQL. Aqui vou cobrir apenas o esqueleto fundamental — projeção, filtro, ordenação, distinção e limite. Junções, agrupamentos e subconsultas ficam para o módulo 10, então não estranhe se eu parar antes do JOIN.
A ordem em que escrevemos as cláusulas é fixa, e convém memorizá-la:
SELECT e FROM: a projeção
O SELECT escolhe quais colunas retornar (a projeção), e o FROM indica a origem.
O * traz todas as colunas. É prático para explorar, mas em código de produção prefira listar as colunas explicitamente: fica mais legível e evita surpresas se a tabela mudar.
Suponha que a consulta SELECT nome, mensalidade FROM estudantes; devolva:
| nome | mensalidade |
|---|---|
| João Silva | 1320.00 |
| Maria Souza | 1320.00 |
| Ana Lima | 950.50 |
WHERE: o filtro
O WHERE restringe quais linhas entram no resultado, aplicando uma condição lógica linha a linha.
Quero chamar sua atenção para o último exemplo. Para testar ausência de valor, usamos IS NULL e IS NOT NULL, nunca = NULL. Isso acontece porque NULL não é um valor comum: ele representa “desconhecido”, e qualquer comparação com ele resulta em NULL, que não é verdadeiro. Essa lógica de três valores (verdadeiro, falso, desconhecido) é um tema delicado e muito cobrado.
| Operador | Significado | Exemplo |
|---|---|---|
= <> < > <= >= |
comparação | idade >= 18 |
AND OR NOT |
conectores lógicos | a AND b |
BETWEEN |
faixa inclusiva | nota BETWEEN 7 AND 10 |
IN |
pertence a conjunto | uf IN ('SP','RJ') |
LIKE |
padrão textual | nome LIKE 'Jo%' |
IS NULL |
ausência de valor | email IS NULL |
Sobre o LIKE. No padrão de texto, % casa qualquer sequência de caracteres e _ casa exatamente um caractere. Assim, 'A%' pega tudo que começa com A, '%a' tudo que termina em a, e '_aria' pega Maria, Daria e similares de cinco letras. No PostgreSQL existe ainda o ILIKE, que ignora maiúsculas e minúsculas.
ORDER BY: a ordenação
O resultado de uma consulta não tem ordem garantida a menos que você peça. O ORDER BY impõe a ordenação, crescente por padrão (ASC) ou decrescente (DESC).
Aqui ordeno primeiro pela mensalidade, da maior para a menor, e em caso de empate desempato pelo nome em ordem alfabética. Ordenar por mais de uma coluna assim é comum e elegante.
DISTINCT: eliminando duplicatas
O DISTINCT remove linhas repetidas do resultado, devolvendo apenas valores únicos.
Se cinco estudantes pertencem ao curso 1 e três ao curso 2, sem o DISTINCT viriam oito linhas; com ele, apenas duas — 1 e 2. É a forma direta de descobrir quais cursos têm ao menos um estudante.
LIMIT: restringindo a quantidade
O LIMIT corta o resultado a um número máximo de linhas, recurso indispensável para paginação e para espiar amostras de tabelas enormes.
A primeira consulta devolve as três maiores mensalidades — repare como ORDER BY e LIMIT trabalham juntos para responder “os top N”. A segunda usa OFFSET para pular as dez primeiras linhas e trazer as cinco seguintes, que é exatamente o mecanismo por trás da navegação por páginas em uma aplicação.
graph LR
A[FROM tabela] --> B[WHERE filtra linhas]
B --> C[SELECT projeta colunas]
C --> D[DISTINCT remove duplicatas]
D --> E[ORDER BY ordena]
E --> F[LIMIT corta]
Esse diagrama mostra a ordem lógica de processamento de uma consulta, que é diferente da ordem em que escrevemos as cláusulas. Primeiro o banco identifica a origem, depois filtra, então projeta as colunas, elimina duplicatas, ordena e por fim aplica o limite. Entender essa sequência ajuda a prever o resultado de consultas que parecem ambíguas.
DCL: controlando o acesso
A DCL trata de segurança: quem pode fazer o quê. No PostgreSQL, usuários e grupos são modelados como roles. Os dois comandos são GRANT, que concede privilégios, e REVOKE, que os retira.
A leitura é direta: o primeiro comando deixa a secretaria consultar e inserir em estudantes; o segundo dá poder total sobre cursos a um administrador; o terceiro tira da secretaria a permissão de inserir; e o último libera leitura para todos, já que PUBLIC representa qualquer role. Esse controle granular é o que permite que o banco seja compartilhado por muitos perfis sem que um interfira indevidamente no domínio do outro.
| Comando | Função | Exemplo |
|---|---|---|
GRANT |
concede privilégio | GRANT SELECT ON t TO r |
REVOKE |
remove privilégio | REVOKE SELECT ON t FROM r |
TCL: agrupando operações em transações
Por fim, a TCL, que gerencia transações — blocos de comandos que devem ser tratados como uma unidade indivisível. Vou apenas apresentar os comandos aqui; o estudo a fundo das propriedades ACID e dos níveis de isolamento espera por você no módulo 12.
A ideia é simples e poderosa: dentro de uma transação, ou tudo dá certo e é confirmado, ou algo falha e tudo é desfeito. Imagine uma transferência de uma matrícula que envolve apagar um registro e inserir outro; você não quer que o primeiro passo seja efetivado e o segundo falhe, deixando o banco inconsistente.
O BEGIN abre a transação, os comandos no meio são provisórios, e o COMMIT confirma tudo de uma vez, tornando as mudanças permanentes e visíveis para os demais usuários. Se algo der errado antes do COMMIT, usamos ROLLBACK:
O ROLLBACK aborta a transação e devolve o banco ao estado anterior ao BEGIN, como se nada tivesse acontecido. E quando queremos um desfazer parcial, entra o SAVEPOINT, que marca um ponto intermediário ao qual podemos retornar sem cancelar a transação inteira:
Acompanhe a lógica: insiro Robótica, marco ponto1, tento inserir um curso inválido, volto ao ponto1 desfazendo só essa última inserção, insiro Data Science e finalmente confirmo. Ao final, Robótica e Data Science persistem; o curso inválido nunca existiu. O SAVEPOINT dá granularidade fina ao controle transacional.
stateDiagram-v2
[*] --> Ativa : BEGIN
Ativa --> Ativa : INSERT UPDATE DELETE
Ativa --> Ponto : SAVEPOINT
Ponto --> Ativa : ROLLBACK TO
Ativa --> Confirmada : COMMIT
Ativa --> Abortada : ROLLBACK
Confirmada --> [*]
Abortada --> [*]
Detalhe do PostgreSQL. Por padrão, no PostgreSQL cada comando isolado roda em modo autocommit, ou seja, é confirmado imediatamente. As transações explícitas com BEGIN existem justamente para agrupar vários comandos sob um único COMMIT ou ROLLBACK. Sem o BEGIN, cada INSERT ou UPDATE já é definitivo no instante em que executa.
Síntese para a prova
Feche este módulo com o mapa das cinco sublinguagens fixado: a DDL define estrutura (CREATE, ALTER, DROP, TRUNCATE), a DML manipula dados (INSERT, UPDATE, DELETE), a DQL consulta (SELECT), a DCL controla acesso (GRANT, REVOKE) e a TCL gerencia transações (BEGIN, COMMIT, ROLLBACK, SAVEPOINT). A pegadinha mais frequente é a confusão entre DELETE, TRUNCATE e DROP: o primeiro é DML e seletivo via WHERE; o segundo é DDL, esvazia tudo rapidamente sem filtro e preserva a estrutura; o terceiro é DDL e elimina o objeto inteiro. Lembre que TRUNCATE não pertence à DML, embora apague dados.
No SELECT, domine a ordem das cláusulas — SELECT, FROM, WHERE, ORDER BY, LIMIT — e a diferença entre a ordem de escrita e a ordem lógica de processamento, que começa pelo FROM. Guarde que NULL exige IS NULL e nunca = NULL, que DISTINCT elimina duplicatas e que LIMIT com OFFSET é a base da paginação. Na história, ancore as datas: Codd em 1970, SEQUEL em 1974, renomeação para SQL em 1977 e primeiro padrão ANSI em 1986. E não esqueça que, para dinheiro, a escolha correta é NUMERIC, jamais um tipo de ponto flutuante. Com esses pontos firmes, você responde com segurança a praticamente qualquer questão de fundamentos de SQL.