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.

Você vai aprender
  • O que é a chave primária: única (UNIQUE) e obrigatória (NOT NULL), uma por tabela.
  • Como escrever PRIMARY KEY e o erro real quando o id repete.
  • INTEGER PRIMARY KEY: o id que o SQLite preenche sozinho.
  • O rowid, a numeração escondida das tabelas do SQLite, e por que INT PRIMARY KEY não é a mesma coisa que INTEGER PRIMARY KEY.
  • A chave composta: PRIMARY KEY (pedido_id, produto_id).
  • As pegadinhas: o datatype mismatch, a chave de texto que aceita NULL e 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:

RegraQuer dizerIgual a
ÚnicaDuas linhas nunca têm a mesma chaveUNIQUE
ObrigatóriaToda linha tem chaveNOT NULL
Uma por tabelaA 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 NULL e o UNIQUE.

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
Não mexa na chave

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 KEYSimSimNão: NULL vira o próximo número
id INT PRIMARY KEYNãoNãoSim (pegadinha do SQLite)
codigo TEXT PRIMARY KEYNãoNãoSim (pegadinha do SQLite)
codigo TEXT NOT NULL PRIMARY KEYNãoNãoNã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

FormaExemploQuando usar
Na colunaid INTEGER PRIMARY KEYO caso comum: um número que se numera sozinho
Restrição de tabela, uma colunaPRIMARY KEY (id) no fim da listaDá no mesmo que a forma da coluna (também vira rowid, se o tipo for INTEGER)
CompostaPRIMARY KEY (pedido_id, produto_id)Tabelas que ligam duas outras, como itens_pedido (aula Relação Muitos-para-Muitos)
Chave de textocodigo TEXT NOT NULL PRIMARY KEYQuando já existe um código natural e estável (a sigla da UF, o código de um cupom)
Com AUTOINCREMENTid INTEGER PRIMARY KEY AUTOINCREMENTQuando um número apagado nunca pode voltar (aula AUTOINCREMENT)
Chave "natural" ou "artificial"?

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

Chave repetida

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

Texto na chave 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.

Duas chaves primárias
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.

Chave composta repetida

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.

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

Nos outros bancos

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

EscritaO que faz
id INTEGER PRIMARY KEYChave que é o rowid: única, nunca NULL, numerada sozinha
codigo TEXT NOT NULL PRIMARY KEYChave de texto (escreva o NOT NULL)
PRIMARY KEY (a, b)Chave composta: o par não se repete
INSERT … RETURNING idMostra 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 SQLiteQuer dizer
UNIQUE constraint failed: categorias.idA chave já existe
UNIQUE constraint failed: itens_pedido.pedido_id, itens_pedido.produto_idA chave composta já existe
datatype mismatchUm valor que não é inteiro numa INTEGER PRIMARY KEY
table "t" has more than one primary keyDuas colunas com PRIMARY KEY
NOT NULL constraint failed: t.cNULL 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:

id

2. A chave composta

Complete a chave da tabela que guarda os produtos de cada pedido:

(pedido_id, produto_id)

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 do rowid: quem não informa o id (ou manda NULL) 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 rowid escondido; SELECT rowid, * o revela.
  • Só o tipo exato INTEGER vira rowid. INT PRIMARY KEY e chaves de texto não se numeram e, por um defeito antigo do SQLite, aceitam NULL: escreva NOT NULL junto.
  • Chave composta: PRIMARY KEY (pedido_id, produto_id), no fim da lista de colunas; é o par que não se repete.