SQL PRIMARY KEY
Na Loja Aurora há duas clientes chamadas Ana, e várias pessoas moram em Teresina. Como o banco sabe de qual cliente um pedido é? Pelo id: a cliente 3 é a Ana Beatriz Sousa, e só ela é a 3. Essa coluna que identifica cada linha, sem nunca repetir e sem nunca ficar vazia, é a chave primária (em inglês, primary key). Todas as tabelas da Loja Aurora têm uma. Nesta aula você vai entender a regra, ver o id se numerar sozinho, descobrir o rowid escondido em toda tabela do SQLite e montar uma chave de duas colunas, como a de itens_pedido.
- O que é a chave primária: única (UNIQUE) e obrigatória (NOT NULL), uma por tabela.
- Como escrever
PRIMARY KEYe o erro real quando oidrepete. INTEGER PRIMARY KEY: oidque o SQLite preenche sozinho.- O
rowid, a numeração escondida das tabelas do SQLite, e por queINT PRIMARY KEYnão é a mesma coisa queINTEGER PRIMARY KEY. - A chave composta:
PRIMARY KEY (pedido_id, produto_id). - As pegadinhas: o
datatype mismatch, a chave de texto que aceitaNULLe o número que volta a ser usado.
A ideia: o CPF de cada linha
No Brasil, duas pessoas podem ter o mesmo nome, nascer no mesmo dia e morar na mesma rua. Mas cada uma tem um CPF só dela, e todo mundo tem um. O CPF não diz nada sobre a pessoa; ele só serve para apontar para ela sem confusão.
A chave primária é o CPF de cada linha. Ela tem três regras:
| Regra | Quer dizer | Igual a |
|---|---|---|
| Única | Duas linhas nunca têm a mesma chave | UNIQUE |
| Obrigatória | Toda linha tem chave | NOT NULL |
| Uma por tabela | A tabela tem uma chave primária (que pode ter mais de uma coluna) | — |
Com isso, a chave vira o "endereço" de cada linha. Quando um pedido diz cliente_id = 3, aponta para uma cliente e só uma. É assim que as tabelas se relacionam (aula Tabelas que se Relacionam) e é a chave que outras tabelas citam com FOREIGN KEY.
1. A chave primária da Loja Aurora
Toda tabela da loja começa assim:
SELECT sql FROM sqlite_schema WHERE name = 'categorias';
- id
- O nome da coluna.
id(de "identificador") é o nome mais comum para a chave. - INTEGER
- O tipo: número inteiro. No SQLite, este tipo exato tem um poder especial (seção 3).
- PRIMARY KEY
- "Chave primária." A restrição vem depois do tipo, como o
NOT NULLe oUNIQUE.
O PRAGMA table_info mostra a chave na coluna pk (de primary key): 1 na coluna da chave, 0 nas outras.
SELECT name, type, pk FROM pragma_table_info('categorias');
2. A chave não repete
A categoria 3 já existe (é "Casa e Cozinha"). A Ana tentou cadastrar outra categoria com o mesmo número:
INSERT INTO categorias (id, nome) VALUES (3, 'Papelaria Fina');
A mensagem é a mesma do UNIQUE, UNIQUE constraint failed: categorias.id: por dentro, a chave primária é uma regra de "único". O mesmo vale para um UPDATE que tente dar a uma categoria o número de outra:
UPDATE categorias SET id = 1 WHERE id = 2;
No terminal, o prefixo Runtime error: e o código (19), o de "restrição violada":
$ sqlite3 loja.db SQLite version 3.50.4 2025-07-30 19:33:53 Enter ".help" for usage hints. sqlite> INSERT INTO categorias (id, nome) VALUES (3, 'Papelaria Fina'); Runtime error: UNIQUE constraint failed: categorias.id (19) sqlite> .quit
Trocar o id de uma linha que já existe é quase sempre má ideia: outras tabelas apontam para ele. Na Loja Aurora, o banco nem deixa: tente UPDATE categorias SET id = 50 WHERE id = 8; e o resultado é FOREIGN KEY constraint failed, porque há produtos da categoria 8. Uma chave boa é escolhida uma vez e nunca muda.
3. O id que se numera sozinho
Se a chave não pode repetir, como a Ana descobre o próximo número livre? Ela não precisa: numa coluna INTEGER PRIMARY KEY, basta não informar o id, e o SQLite escolhe.
INSERT INTO categorias (nome) VALUES ('Pet Shop');
SELECT * FROM categorias WHERE id > 6;
A categoria nova ganhou o 9, um a mais que o maior id da tabela (8). Mandar NULL no id dá o mesmo resultado: numa INTEGER PRIMARY KEY, NULL quer dizer "escolha você".
INSERT INTO categorias (id, nome) VALUES (NULL, 'Pet Shop'); SELECT id, nome FROM categorias WHERE nome = 'Pet Shop';
Como saber o número que o banco escolheu? O RETURNING id ("devolva o id"), no fim do INSERT, mostra na hora:
INSERT INTO categorias (nome) VALUES ('Pet Shop') RETURNING id;
A regra do "um a mais que o maior" tem consequências. Se a Ana cadastrar a categoria 100 à mão, a próxima automática será 101, e não 10:
INSERT INTO categorias (id, nome) VALUES (100, 'Pet Shop');
INSERT INTO categorias (nome) VALUES ('Moda');
SELECT id, nome FROM categorias WHERE id > 8;
E se a linha de id mais alto for apagada, o número dela pode voltar a ser usado. Numa tabela nova de avaliações, a 3 foi apagada e a próxima avaliação ganhou o 3 de novo:
CREATE TABLE avaliacoes (
id INTEGER PRIMARY KEY,
produto_id INTEGER NOT NULL
);
INSERT INTO avaliacoes (produto_id) VALUES (3), (1), (10);
DELETE FROM avaliacoes WHERE id = 3;
INSERT INTO avaliacoes (produto_id) VALUES (5);
SELECT * FROM avaliacoes;
Se um número reaproveitado causaria confusão (um sistema antigo que ainda guarda "avaliação 3" apontando para a avaliação apagada), use AUTOINCREMENT, que garante números nunca repetidos: é a próxima aula, AUTOINCREMENT.
Experimente: a Ana quer cadastrar a categoria "Música", mas chutou o id 5, que já é usado. Rode e veja o erro. Depois conserte, deixando o banco escolher o número.
INSERT INTO categorias (id, nome) VALUES (5, 'Música'); SELECT * FROM categorias;
INSERT INTO categorias (nome) VALUES ('Música');
SELECT * FROM categorias;
4. O rowid: a chave escondida
Agora um segredo do SQLite: toda tabela comum já tem uma numeração, mesmo sem chave primária. É uma coluna escondida chamada rowid ("id da linha"). Ela não aparece no SELECT *, mas está lá:
CREATE TABLE recados (texto TEXT);
INSERT INTO recados VALUES ('Repor lápis'), ('Ligar para o fornecedor');
SELECT * FROM recados;
SELECT rowid, * FROM recados;
O primeiro SELECT mostra só o texto. O segundo, pedindo o rowid pelo nome, revela os números 1 e 2. É por ele que o SQLite guarda e encontra as linhas por dentro. A tabela itens_pedido da loja, que não tem uma coluna id, também tem o seu:
SELECT rowid, pedido_id, produto_id FROM itens_pedido LIMIT 3;
INTEGER PRIMARY KEY é o próprio rowid
Aqui está o "poder especial" da seção 1. Quando a chave primária é uma coluna só, do tipo escrito exatamente INTEGER, ela vira um outro nome (um "apelido") para o rowid. Não são duas numerações: é uma só, com dois nomes. Veja o que acontece quando a Ana pede as duas colunas na tabela categorias:
SELECT rowid, id, nome FROM categorias LIMIT 3;
Repare no cabeçalho: as duas primeiras colunas se chamam id. O SQLite pediu o rowid, viu que ele tem um nome "de verdade" nessa tabela, e mostrou esse nome. É por ser o rowid que o id se numera sozinho: o rowid sempre teve essa numeração automática.
A pegadinha: INT não é INTEGER
A documentação do SQLite é clara: só o nome de tipo exato INTEGER faz a chave virar rowid. Com INT, BIGINT ou qualquer outro, a chave é uma coluna comum, com uma regra de único, e não se numera sozinha:
CREATE TABLE avaliacoes (id INT PRIMARY KEY, comentario TEXT);
INSERT INTO avaliacoes (comentario) VALUES ('Chegou rápido'), ('Bom');
SELECT rowid, id, comentario FROM avaliacoes;
Agora o rowid e o id são colunas diferentes: o rowid (escondido) numerou 1 e 2, e o id ficou NULL nas duas linhas. Duas linhas com a chave vazia! Isso leva à próxima pegadinha.
| Declaração | É o rowid? | Numera sozinho? | Aceita NULL? |
|---|---|---|---|
id INTEGER PRIMARY KEY | Sim | Sim | Não: NULL vira o próximo número |
id INT PRIMARY KEY | Não | Não | Sim (pegadinha do SQLite) |
codigo TEXT PRIMARY KEY | Não | Não | Sim (pegadinha do SQLite) |
codigo TEXT NOT NULL PRIMARY KEY | Não | Não | Não |
Experimente: as avaliações ficaram com o id NULL, porque a chave foi declarada com INT. Mude uma palavra no CREATE TABLE para que o id se numere sozinho (1 e 2).
CREATE TABLE avaliacoes (
id INT PRIMARY KEY,
produto_id INTEGER NOT NULL,
nota INTEGER NOT NULL
);
INSERT INTO avaliacoes (produto_id, nota) VALUES (3, 5), (1, 4);
SELECT id, produto_id, nota FROM avaliacoes;
CREATE TABLE avaliacoes (
id INTEGER PRIMARY KEY,
produto_id INTEGER NOT NULL,
nota INTEGER NOT NULL
);
INSERT INTO avaliacoes (produto_id, nota) VALUES (3, 5), (1, 4);
SELECT id, produto_id, nota FROM avaliacoes;
5. A chave que aceita NULL (um defeito antigo)
Pelo SQL padrão, chave primária é sempre NOT NULL. O SQLite, por causa de um defeito das primeiras versões, deixa passar NULL na chave primária, a não ser que ela seja INTEGER PRIMARY KEY, que a tabela seja STRICT (ou WITHOUT ROWID) ou que a coluna tenha NOT NULL escrito. A documentação explica que consertar isso quebraria programas antigos, e por isso o comportamento ficou.
CREATE TABLE cupons (codigo TEXT PRIMARY KEY, desconto REAL); INSERT INTO cupons VALUES (NULL, 10), (NULL, 15); SELECT * FROM cupons;
Dois cupons sem código, com a chave primária vazia. Com NOT NULL, o banco faz o que o padrão manda:
CREATE TABLE cupons (codigo TEXT NOT NULL PRIMARY KEY, desconto REAL); INSERT INTO cupons VALUES (NULL, 10);
A regra prática: se a chave não for INTEGER PRIMARY KEY, escreva NOT NULL junto.
6. A chave composta: duas colunas
A tabela itens_pedido não tem id. Cada linha é "o produto tal, no pedido tal". O que identifica a linha é o par: o pedido 1 pode ter vários produtos, o produto 1 pode estar em vários pedidos, mas o produto 1 aparece uma vez só no pedido 1 (se o cliente leva dois, a quantidade é 2). Uma chave de mais de uma coluna se chama chave composta:
SELECT sql FROM sqlite_schema WHERE name = 'itens_pedido';
- pedido_id … NOT NULL
- As colunas da chave, com
NOT NULL(lembre do defeito da seção 5). - PRIMARY KEY (pedido_id, produto_id)
- A chave vai numa linha própria, no fim, com as colunas entre parênteses: é uma restrição de tabela, igual ao
UNIQUE (a, b)da aula UNIQUE.
O pedido 1 tem os produtos 1, 2 e 3. Pôr o produto 1 de novo no pedido 1 é recusado; já um produto novo no mesmo pedido, ou o mesmo produto em outro pedido, passa:
SELECT pedido_id, produto_id, quantidade FROM itens_pedido WHERE pedido_id = 1; INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario) VALUES (1, 1, 2, 12.35);
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario) VALUES (1, 5, 1, 89.9); SELECT pedido_id, produto_id, quantidade FROM itens_pedido WHERE pedido_id = 1;
No PRAGMA table_info, a coluna pk mostra a ordem de cada coluna dentro da chave: 1 para pedido_id, 2 para produto_id.
SELECT name, pk FROM pragma_table_info('itens_pedido');
Uma chave composta não se numera sozinha: não é INTEGER PRIMARY KEY de uma coluna só. Cada INSERT informa as duas partes.
Formas diferentes de escrever a chave
| Forma | Exemplo | Quando usar |
|---|---|---|
| Na coluna | id INTEGER PRIMARY KEY | O caso comum: um número que se numera sozinho |
| Restrição de tabela, uma coluna | PRIMARY KEY (id) no fim da lista | Dá no mesmo que a forma da coluna (também vira rowid, se o tipo for INTEGER) |
| Composta | PRIMARY KEY (pedido_id, produto_id) | Tabelas que ligam duas outras, como itens_pedido (aula Relação Muitos-para-Muitos) |
| Chave de texto | codigo TEXT NOT NULL PRIMARY KEY | Quando já existe um código natural e estável (a sigla da UF, o código de um cupom) |
Com AUTOINCREMENT | id INTEGER PRIMARY KEY AUTOINCREMENT | Quando um número apagado nunca pode voltar (aula AUTOINCREMENT) |
Um CPF ou um email parecem ótimas chaves, porque já são únicos. Mas a pessoa pode mudar de email, e um CPF digitado errado precisa ser corrigido, e a chave não deveria mudar nunca. Por isso quase todo projeto usa uma chave artificial, um id INTEGER PRIMARY KEY sem significado, e põe o email numa coluna UNIQUE separada. É o que a Loja Aurora faz.
Quando dá errado
UNIQUE constraint failed: categorias.id. Como consertar: não informe o id e deixe o banco escolher, ou confira o maior número usado com SELECT max(id) FROM categorias;.
INTEGER PRIMARY KEY
INSERT INTO categorias (id, nome) VALUES ('nove', 'Pet Shop');
O que diz: datatype mismatch, "tipo de dado não combina". Lembra que o SQLite aceita texto numa coluna INTEGER comum (aula Os Tipos no SQLite)? Na INTEGER PRIMARY KEY, não: ela é o rowid, que só pode ser inteiro. O texto '20' passa (vira o número 20); 'nove' e 2.5 não. No terminal, o código muda para (20), o de "tipo errado":
$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> INSERT INTO categorias (id, nome) VALUES ('nove', 'Pet Shop');
Runtime error: datatype mismatch (20)
sqlite> .quit
Como consertar: a chave é um número inteiro. Ou, melhor, não a informe.
CREATE TABLE cupons (
id INTEGER PRIMARY KEY,
codigo TEXT PRIMARY KEY
);
O que diz: table "cupons" has more than one primary key, "a tabela tem mais de uma chave primária". Como consertar: escolha uma para ser a chave (o id) e faça a outra UNIQUE: codigo TEXT NOT NULL UNIQUE. Se a ideia era uma chave de duas colunas, escreva PRIMARY KEY (a, b) no fim.
Já viu: UNIQUE constraint failed: itens_pedido.pedido_id, itens_pedido.produto_id. A mensagem lista as duas colunas da chave. Como consertar: se o cliente levou mais unidades, não é um item novo: aumente a quantidade do item que existe com UPDATE.
id ficou NULL
A chave foi declarada como INT PRIMARY KEY (ou BIGINT, ou de texto), e o INSERT não informou o id. Como consertar: para numerar sozinho, o tipo tem de ser exatamente INTEGER. Para outras chaves, junte NOT NULL, e o erro aparece na hora: NOT NULL constraint failed: cupons.codigo.
PRIMARY KEY é do SQL padrão e se escreve igual no MySQL, no PostgreSQL e no SQL Server, na coluna ou como PRIMARY KEY (a, b). Neles, a chave primária é sempre NOT NULL (a documentação do PostgreSQL diz: PRIMARY KEY exige valores únicos e não nulos, e uma tabela tem no máximo uma). A grande diferença é a numeração automática, que no SQLite vem de graça com o INTEGER PRIMARY KEY e nos outros precisa ser pedida (trechos ilustrativos, não rodam no SQLite):
CREATE TABLE avaliacoes (
id INT NOT NULL AUTO_INCREMENT,
nota INT NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE avaliacoes (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nota integer NOT NULL
);
CREATE TABLE avaliacoes (
id INT IDENTITY(1,1) PRIMARY KEY,
nota INT NOT NULL
);
E o rowid escondido é coisa do SQLite: os outros bancos não têm uma coluna com esse nome. Mais sobre a numeração automática na próxima aula, AUTOINCREMENT.
Referência rápida
| Escrita | O que faz |
|---|---|
id INTEGER PRIMARY KEY | Chave que é o rowid: única, nunca NULL, numerada sozinha |
codigo TEXT NOT NULL PRIMARY KEY | Chave de texto (escreva o NOT NULL) |
PRIMARY KEY (a, b) | Chave composta: o par não se repete |
INSERT … RETURNING id | Mostra o id que o banco escolheu |
SELECT rowid, * FROM t; | Mostra a coluna escondida rowid |
SELECT name, pk FROM pragma_table_info('t'); | Quais colunas formam a chave, e em que ordem |
| Mensagem do SQLite | Quer dizer |
|---|---|
UNIQUE constraint failed: categorias.id | A chave já existe |
UNIQUE constraint failed: itens_pedido.pedido_id, itens_pedido.produto_id | A chave composta já existe |
datatype mismatch | Um valor que não é inteiro numa INTEGER PRIMARY KEY |
table "t" has more than one primary key | Duas colunas com PRIMARY KEY |
NOT NULL constraint failed: t.c | NULL numa chave com NOT NULL |
Exercícios
1. A chave que se numera sozinha
Complete para que o id seja a chave primária e se numere sozinho no SQLite:
2. A chave composta
Complete a chave da tabela que guarda os produtos de cada pedido:
3. A tabela categorias tem ids de 1 a 8. Qual id recebe INSERT INTO categorias (nome) VALUES ('Pet Shop');?
4. Qual destas declarações faz a coluna virar um apelido do rowid?
5. Em itens_pedido, com PRIMARY KEY (pedido_id, produto_id), o pedido 1 já tem o produto 1. Qual INSERT é recusado?
6. Por que INSERT INTO categorias (id, nome) VALUES ('nove', 'Pet Shop'); dá datatype mismatch?
Missão final: o carrinho de compras
Missão: o carrinho guarda, para cada cliente, os produtos que ele separou e a quantidade. O mesmo produto não pode aparecer duas vezes no carrinho do mesmo cliente (para isso existe a quantidade). Rode: o quarto INSERT repetiu o par cliente 5, produto 3, e a tabela aceitou. Dê ao carrinho a chave primária composta certa, para que esse INSERT seja recusado. O erro em vermelho é a resposta certa.
CREATE TABLE carrinho (
cliente_id INTEGER NOT NULL,
produto_id INTEGER NOT NULL,
quantidade INTEGER NOT NULL DEFAULT 1
);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (5, 3);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (5, 1);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (8, 3);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (5, 3);
SELECT * FROM carrinho;
CREATE TABLE carrinho (
cliente_id INTEGER NOT NULL,
produto_id INTEGER NOT NULL,
quantidade INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (cliente_id, produto_id)
);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (5, 3);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (5, 1);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (8, 3);
INSERT INTO carrinho (cliente_id, produto_id) VALUES (5, 3);
SELECT * FROM carrinho;
Depois de cumprir, troque o último INSERT por UPDATE carrinho SET quantidade = quantidade + 1 WHERE cliente_id = 5 AND produto_id = 3;: é assim que um carrinho de verdade registra "mais um".
Resumo
- A chave primária identifica cada linha: é única, obrigatória, e só há uma por tabela. A repetição dá
UNIQUE constraint failed: tabela.coluna. id INTEGER PRIMARY KEYé um apelido dorowid: quem não informa oid(ou mandaNULL) ganha o próximo número, um a mais que o maior. Texto que não é número dádatatype mismatch.- Toda tabela comum do SQLite tem o
rowidescondido;SELECT rowid, *o revela. - Só o tipo exato
INTEGERvirarowid.INT PRIMARY KEYe chaves de texto não se numeram e, por um defeito antigo do SQLite, aceitamNULL: escrevaNOT NULLjunto. - Chave composta:
PRIMARY KEY (pedido_id, produto_id), no fim da lista de colunas; é o par que não se repete.