SQL UNIQUE
A Loja Aurora vai lançar cupons de desconto: AURORA10 dá 10%, VOLTA15 dá 15%. O que acontece se alguém, sem querer, cadastra um segundo AURORA10 com 20%? Na hora de usar o cupom, qual dos dois vale? Para que isso nunca aconteça, existe a restrição UNIQUE ("único"): numa coluna UNIQUE, cada valor só pode aparecer uma vez. Nesta aula você vai usá-la, vai ver o erro real do SQLite com valores repetidos e vai conhecer os casos que confundem: o NULL que se repete à vontade, as maiúsculas, e a regra que vale para duas colunas juntas.
- Como escrever
UNIQUEnuma coluna, e o erroUNIQUE constraint failed: tabela.coluna. - O
UNIQUEnoINSERT, noUPDATEe num lote de várias linhas. UNIQUE (coluna1, coluna2): quando o que não pode repetir é a combinação.- Por que vários
NULLpassam, e por que'AURORA10'e'aurora10'são diferentes (e como mudar isso comCOLLATE NOCASE). - Como pôr a regra numa tabela que já existe (
CREATE UNIQUE INDEX) e como lidar com a repetição sem erro (INSERT OR IGNORE,ON CONFLICT).
A ideia: o número da camisa
Num time de futebol, dois jogadores podem ter o mesmo nome, a mesma idade, a mesma cidade. Mas não podem ter o mesmo número de camisa: o juiz precisa saber quem é o 10. Se o técnico tenta inscrever um segundo 10, a federação recusa a inscrição.
Numa tabela, o UNIQUE é essa regra do número da camisa. Ele vale para colunas que identificam alguma coisa: o código de um cupom, o email de login de um cliente, o CPF, a placa de um carro. Nomes e cidades, ao contrário, se repetem normalmente: a Loja Aurora tem 10 clientes em Teresina.
| Coluna | Pode repetir? | UNIQUE? |
|---|---|---|
| Código do cupom | Não: cada código é um cupom | Sim |
| Email de login | Não: cada email é uma conta | Sim |
| Nome do cliente | Sim: existem dois "José Silva" | Não |
| Cidade | Sim: muitos clientes por cidade | Não |
1. A primeira coluna UNIQUE
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT INTO cupons VALUES ('AURORA10', 15);
O primeiro INSERT entrou ("1 linha afetada"). O segundo foi recusado:
- codigo TEXT NOT NULL UNIQUE
- A coluna é texto, obrigatória (NOT NULL) e única. As restrições vêm depois do tipo, em qualquer ordem.
- UNIQUE constraint failed
- "A restrição UNIQUE falhou": o valor já existe na coluna.
- cupons.codigo
- Onde: tabela
cupons, colunacodigo.
Repare que a mensagem não diz qual valor repetiu. Num lote grande, você vai ter de procurar; a consulta da seção 6 ajuda. No terminal, a mesma mensagem, com 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> CREATE TABLE cupons (codigo TEXT NOT NULL UNIQUE, desconto REAL NOT NULL);
sqlite> INSERT INTO cupons VALUES ('AURORA10', 10);
sqlite> INSERT INTO cupons VALUES ('AURORA10', 15);
Runtime error: UNIQUE constraint failed: cupons.codigo (19)
sqlite> .quit
Experimente: esta tabela aceitou dois cupons VOLTA15 (rode e veja). Mude o CREATE TABLE para que o banco recuse o segundo. O erro em vermelho é a resposta certa.
CREATE TABLE cupons (
codigo TEXT NOT NULL,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('VOLTA15', 15);
INSERT INTO cupons VALUES ('VOLTA15', 25);
SELECT * FROM cupons;
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('VOLTA15', 15);
INSERT INTO cupons VALUES ('VOLTA15', 25);
SELECT * FROM cupons;
2. No UPDATE e nos lotes
A regra vale para qualquer mudança. Um UPDATE que transforma o VOLTA15 em AURORA10 criaria uma repetição, e é recusado:
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10), ('VOLTA15', 15);
UPDATE cupons SET codigo = 'AURORA10' WHERE codigo = 'VOLTA15';
E num INSERT de várias linhas, a repetição pode estar dentro do próprio lote. Como com o NOT NULL, uma linha ruim derruba o comando inteiro: nenhuma das três entra.
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10), ('VOLTA15', 15), ('AURORA10', 20);
3. O que conta como "igual"
Para o UNIQUE, dois valores são repetidos se forem iguais para o banco. E isso tem surpresas.
Maiúsculas, minúsculas e espaços contam
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10), ('aurora10', 15), ('AURORA10 ', 5);
SELECT codigo, length(codigo) AS letras FROM cupons;
Três cupons, todos aceitos. Para os olhos é o mesmo código; para o SQLite são três textos diferentes: um em maiúsculas, um em minúsculas e um com um espaço no fim (repare nas 9 letras). Se um cliente digitar aurora10, qual desconto ele ganha? Para comparar textos sem ligar para maiúsculas, a coluna pode ter COLLATE NOCASE ("compare sem diferenciar maiúsculas"):
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE COLLATE NOCASE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT INTO cupons VALUES ('aurora10', 15);
Agora 'aurora10' conta como repetido. O NOCASE do SQLite só sabe igualar as letras sem acento (de A a Z): 'É' e 'é' continuam diferentes para ele. Para códigos como AURORA10, isso basta. Os espaços sobrando não são resolvidos pelo NOCASE: tire-os antes de gravar, com trim() (aula Funções de Texto).
Números: 1, '1' e 1.0
Numa coluna INTEGER UNIQUE, o texto '1' vira o número 1 ao entrar (é a afinidade, da aula Os Tipos no SQLite), e 1.0 também é igual a 1. Os três contam como repetidos:
CREATE TABLE senhas_fila (numero INTEGER UNIQUE);
INSERT INTO senhas_fila VALUES (1), ('1');
NULL não é igual a nada
Lembra da aula O Valor NULL? NULL quer dizer "não sei", e um "não sei" nunca é igual a outro "não sei". Por isso, numa coluna UNIQUE sem NOT NULL, vários NULL entram sem problema:
CREATE TABLE cupons (
codigo TEXT UNIQUE,
desconto REAL
);
INSERT INTO cupons VALUES (NULL, 10), (NULL, 15);
SELECT * FROM cupons;
Às vezes é isso que você quer: pense numa coluna cpf UNIQUE numa loja em que o CPF é opcional. Quem informou, informou um CPF só seu; quem não informou fica com NULL, e podem ser muitos. Se o valor é obrigatório, junte o NOT NULL, como no código do cupom.
4. UNIQUE em duas colunas juntas
A Ana quer uma regra para as avaliações: cada cliente avalia cada produto uma vez só. A cliente 5 pode avaliar vários produtos; o produto 3 pode ser avaliado por vários clientes; o que não pode é a cliente 5 avaliar o produto 3 duas vezes. Nem cliente_id nem produto_id são únicos sozinhos: o que é único é o par.
Para isso, o UNIQUE vai numa linha própria, no fim da lista de colunas, com as colunas entre parênteses. É uma restrição de tabela (vale para a tabela, não para uma coluna só):
CREATE TABLE avaliacoes (
id INTEGER,
cliente_id INTEGER NOT NULL,
produto_id INTEGER NOT NULL,
nota INTEGER NOT NULL,
UNIQUE (cliente_id, produto_id)
);
INSERT INTO avaliacoes VALUES (1, 5, 3, 5), -- cliente 5, produto 3
(2, 5, 1, 4), -- cliente 5, produto 1: outro produto, ok
(3, 8, 3, 2); -- cliente 8, produto 3: outro cliente, ok
INSERT INTO avaliacoes VALUES (4, 5, 3, 1); -- cliente 5, produto 3 de novo!
- UNIQUE (cliente_id, produto_id)
- A combinação das duas colunas não pode repetir. Vem depois da última coluna, separada por vírgula, como se fosse mais uma linha da lista.
- 3 linhas afetadas
- O primeiro lote entrou inteiro: nenhum par se repete (5-3, 5-1, 8-3).
- avaliacoes.cliente_id, avaliacoes.produto_id
- A mensagem lista as duas colunas da regra: o par 5-3 já existe.
Na tabela itens_pedido, a mesma ideia aparece como PRIMARY KEY (pedido_id, produto_id): um produto aparece uma vez só em cada pedido (se o cliente leva dois, a quantidade é 2). A chave primária é parente próxima do UNIQUE, e é o assunto da próxima aula, PRIMARY KEY.
Experimente: a Ana errou a regra: pôs UNIQUE só no cliente_id, e agora a cliente 5 não consegue avaliar um segundo produto (rode e veja o erro). Troque pela regra certa, a do par cliente-produto, para que as três avaliações entrem. O PRAGMA index_list no fim mostra o índice que a regra cria (o 1 e o u dizem "único" e "veio de um UNIQUE").
CREATE TABLE avaliacoes (
id INTEGER,
cliente_id INTEGER NOT NULL UNIQUE,
produto_id INTEGER NOT NULL,
nota INTEGER NOT NULL
);
INSERT INTO avaliacoes VALUES (1, 5, 3, 5), (2, 5, 1, 4), (3, 8, 3, 2);
PRAGMA index_list(avaliacoes);
CREATE TABLE avaliacoes (
id INTEGER,
cliente_id INTEGER NOT NULL,
produto_id INTEGER NOT NULL,
nota INTEGER NOT NULL,
UNIQUE (cliente_id, produto_id)
);
INSERT INTO avaliacoes VALUES (1, 5, 3, 5), (2, 5, 1, 4), (3, 8, 3, 2);
PRAGMA index_list(avaliacoes);
Cuidado com a diferença: cliente_id INTEGER UNIQUE, produto_id INTEGER UNIQUE (um UNIQUE em cada coluna) são duas regras separadas, e muito mais rígidas: cada cliente avaliaria um único produto na vida, e cada produto teria uma única avaliação. UNIQUE (cliente_id, produto_id) é uma regra sobre o par.
5. Por dentro: o índice
Como o banco descobre, num piscar de olhos, se um código já existe entre milhares? Para cada UNIQUE, o SQLite cria sozinho um índice: uma lista ordenada dos valores da coluna, como o índice remissivo no fim de um livro (aula Índices). Ele aparece na sqlite_schema com um nome automático:
CREATE TABLE cupons (codigo TEXT NOT NULL UNIQUE, desconto REAL NOT NULL); SELECT type, name, tbl_name FROM sqlite_schema WHERE tbl_name = 'cupons';
O sqlite_autoindex_cupons_1 é o índice do UNIQUE: autoindex quer dizer "índice automático", e o 1 é por ser o primeiro da tabela cupons. No terminal, o comando de ponto .indexes cupons lista o mesmo nome.
Pôr a regra numa tabela que já existe
A tabela clientes da Loja Aurora não tem UNIQUE no email. Dá para acrescentar uma coluna UNIQUE com ALTER TABLE? Não no SQLite:
ALTER TABLE clientes ADD COLUMN cpf TEXT UNIQUE;
"Não é possível acrescentar uma coluna UNIQUE." O caminho é criar o índice único você mesmo, com CREATE UNIQUE INDEX, numa coluna que já existe. Antes, confira se há repetidos, porque o índice não nasce se houver:
SELECT email, count(*) AS vezes FROM clientes GROUP BY email HAVING count(*) > 1;
A única "repetição" é o NULL, dos três clientes sem email, e NULL não conta para o UNIQUE. Pode criar:
CREATE UNIQUE INDEX clientes_email_unico ON clientes (email);
INSERT INTO clientes (nome, email)
VALUES ('José R. Silva', 'jose.ribamar@exemplo.com.br');
- CREATE UNIQUE INDEX
- "Crie um índice único."
- clientes_email_unico
- O nome do índice, escolhido por você. Um nome que diga a tabela, a coluna e a regra facilita a vida depois.
- ON clientes (email)
- Em qual tabela e coluna.
- UNIQUE constraint failed: clientes.email
- O índice já trabalha: o email do cliente 1 não pode ser usado de novo.
E se a coluna já tem repetidos? Em cidade, que tem Teresina 10 vezes:
CREATE UNIQUE INDEX clientes_cidade_unica ON clientes (cidade);
O índice não é criado, e a mensagem é a mesma de um INSERT repetido. Limpe os repetidos antes (ou, como aqui, perceba que a coluna não devia ser única).
6. Repetiu: e agora? OR IGNORE e ON CONFLICT
Às vezes a repetição é esperada: um roteiro que cadastra os cupons do mês e pode rodar duas vezes, ou um programa que recebe a mesma avaliação duas vezes por causa de um clique duplo. Em vez de parar tudo com um erro, você pode dizer ao SQLite o que fazer:
| Escrita | Quando o valor já existe |
|---|---|
INSERT INTO … | Erro, e o comando para (o normal) |
INSERT OR IGNORE INTO … | Ignora a linha repetida, sem erro ("0 linhas afetadas") |
INSERT … ON CONFLICT DO NOTHING | O mesmo, na forma que o PostgreSQL também usa |
INSERT … ON CONFLICT (coluna) DO UPDATE SET … | Atualiza a linha que já existe (o famoso upsert: update + insert) |
CREATE TABLE cupons (codigo TEXT NOT NULL UNIQUE, desconto REAL NOT NULL);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT OR IGNORE INTO cupons VALUES ('AURORA10', 15);
SELECT * FROM cupons;
O segundo INSERT deu "0 linhas afetadas" e o cupom ficou com os 10% originais. Já com o upsert, a repetição atualiza o desconto:
CREATE TABLE cupons (codigo TEXT NOT NULL UNIQUE, desconto REAL NOT NULL);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT INTO cupons VALUES ('AURORA10', 15)
ON CONFLICT (codigo) DO UPDATE SET desconto = excluded.desconto;
SELECT * FROM cupons;
- ON CONFLICT (codigo)
- "Se der conflito na coluna
codigo…". A coluna tem de ter umUNIQUE(ou ser a chave primária). - DO UPDATE SET
- "…faça um
UPDATEna linha que já existe". - excluded.desconto
excluded("a excluída") é a linha que oINSERTtentou pôr e não conseguiu: o 15.
Use com cuidado: um erro de repetição muitas vezes avisa um problema de verdade (o cupom que alguém digitou duas vezes com descontos diferentes). Ignorar o erro esconde o aviso.
Formas diferentes de escrever
| Forma | Exemplo | Quando usar |
|---|---|---|
| Na coluna | codigo TEXT NOT NULL UNIQUE | Uma coluna só: a forma mais comum |
| Restrição de tabela | UNIQUE (cliente_id, produto_id) | Quando a regra vale para uma combinação de colunas (e também serve para uma coluna só) |
| Restrição com nome | CONSTRAINT cupom_unico UNIQUE (codigo) | Para documentar; no SQLite a mensagem de erro continua mostrando as colunas, não o nome |
| Índice único | CREATE UNIQUE INDEX clientes_email_unico ON clientes (email); | Tabela que já existe |
| Sem diferenciar maiúsculas | codigo TEXT UNIQUE COLLATE NOCASE | Códigos e emails, em que A e a são a mesma coisa |
Quando dá errado
UNIQUE constraint failed: cupons.codigo. Como consertar: confira se o valor já existe (SELECT * FROM cupons WHERE codigo = 'AURORA10';). Se o cadastro novo está certo e o velho errado, atualize o velho com UPDATE em vez de inserir de novo. Se a repetição é esperada, use INSERT OR IGNORE ou ON CONFLICT.
CREATE TABLE avaliacoes (
id INTEGER,
cliente_id INTEGER UNIQUE,
produto_id INTEGER UNIQUE,
nota INTEGER
);
INSERT INTO avaliacoes VALUES (1, 5, 3, 5), (2, 5, 1, 4);
O que diz: UNIQUE constraint failed: avaliacoes.cliente_id: a cliente 5 não pode ter uma segunda avaliação. Como consertar: uma regra só, para o par: UNIQUE (cliente_id, produto_id).
'AURORA10', 'aurora10' e 'AURORA10 ' entraram os três. Como consertar: COLLATE NOCASE na coluna resolve as maiúsculas; os espaços se tiram com trim() antes de gravar (ou com uma regra CHECK (codigo = trim(codigo)), aula CHECK).
NULL
Vários NULL numa coluna UNIQUE são permitidos. Como consertar: se a coluna é obrigatória, ela precisa também de NOT NULL.
ON CONFLICT numa coluna sem UNIQUE
CREATE TABLE cupons (codigo TEXT NOT NULL UNIQUE, desconto REAL NOT NULL);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT INTO cupons VALUES ('AURORA10', 15) ON CONFLICT (desconto) DO NOTHING;
O que diz: ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint, "a cláusula ON CONFLICT não corresponde a nenhuma restrição PRIMARY KEY ou UNIQUE". O desconto não é único, então nunca dá conflito nele. Como consertar: ON CONFLICT (codigo), a coluna que tem a regra.
UNIQUE no ALTER TABLE
ALTER TABLE clientes ADD COLUMN cpf TEXT UNIQUE; dá Cannot add a UNIQUE column. Como consertar: acrescente a coluna sem UNIQUE e crie depois um CREATE UNIQUE INDEX nela.
UNIQUE é do SQL padrão e se escreve igual no MySQL, no PostgreSQL e no SQL Server, na coluna ou como UNIQUE (a, b). A diferença que mais pega é o NULL, que o próprio padrão deixa para cada banco decidir (a documentação do PostgreSQL avisa disso):
- MySQL e PostgreSQL fazem como o SQLite: vários
NULLpassam. No PostgreSQL, dá para mudar isso comUNIQUE NULLS NOT DISTINCT. - SQL Server é diferente: numa restrição
UNIQUE, a documentação da Microsoft diz que só umNULLé permitido por coluna.
Os três também acrescentam UNIQUE numa tabela existente com ALTER TABLE … ADD CONSTRAINT nome UNIQUE (coluna), coisa que o SQLite não faz (lá, o caminho é o CREATE UNIQUE INDEX).
Referência rápida
| Escrita | O que faz |
|---|---|
coluna TIPO UNIQUE | Cada valor da coluna aparece no máximo uma vez (NULL não conta) |
UNIQUE (a, b) | A combinação de a e b não se repete |
coluna TEXT UNIQUE COLLATE NOCASE | Único sem diferenciar maiúsculas (só de A a Z) |
CREATE UNIQUE INDEX nome ON tabela (coluna); | Põe a regra numa tabela que já existe |
INSERT OR IGNORE INTO … | Pula a linha repetida, sem erro |
INSERT … ON CONFLICT (col) DO UPDATE SET c = excluded.c | Atualiza a linha existente (upsert) |
PRAGMA index_list(tabela); | Lista os índices, inclusive os do UNIQUE |
| Mensagem do SQLite | Quer dizer |
|---|---|
UNIQUE constraint failed: cupons.codigo | O valor já existe na coluna |
UNIQUE constraint failed: avaliacoes.cliente_id, avaliacoes.produto_id | O par já existe |
Cannot add a UNIQUE column | ALTER TABLE … ADD COLUMN … UNIQUE, que o SQLite não aceita |
ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint | O ON CONFLICT apontou uma coluna sem regra de único |
Exercícios
1. Email único
Complete para que dois usuários nunca tenham o mesmo email:
2. O par
Complete a restrição de tabela que impede o mesmo produto duas vezes no mesmo pedido:
3. Numa coluna codigo TEXT UNIQUE (sem NOT NULL), o que acontece com dois INSERT com codigo NULL?
4. A coluna é codigo TEXT UNIQUE, e já existe 'AURORA10'. Qual destes INSERT dá erro?
5. Com UNIQUE (cliente_id, produto_id), qual par pode entrar numa tabela que já tem (5, 3)?
6. Como pôr UNIQUE na coluna email da tabela clientes, que já existe, no SQLite?
Missão final: o roteiro dos cupons
Missão: o roteiro cadastrou o AURORA10 duas vezes (rode e veja os três cupons). Faça duas mudanças: (1) torne o codigo único; (2) troque o terceiro INSERT para que a repetição seja ignorada sem erro, e o roteiro chegue até o fim com dois cupons. O PRAGMA confere se a regra existe.
CREATE TABLE cupons (
codigo TEXT NOT NULL,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT INTO cupons VALUES ('VOLTA15', 15);
INSERT INTO cupons VALUES ('AURORA10', 20);
SELECT * FROM cupons;
PRAGMA index_list(cupons);
CREATE TABLE cupons (
codigo TEXT NOT NULL UNIQUE,
desconto REAL NOT NULL
);
INSERT INTO cupons VALUES ('AURORA10', 10);
INSERT INTO cupons VALUES ('VOLTA15', 15);
INSERT OR IGNORE INTO cupons VALUES ('AURORA10', 20);
SELECT * FROM cupons;
PRAGMA index_list(cupons);
Qual desconto ficou no AURORA10: 10 ou 20? Agora troque o OR IGNORE por um ON CONFLICT (codigo) DO UPDATE SET desconto = excluded.desconto no fim do terceiro INSERT e compare.
Resumo
UNIQUEproíbe valores repetidos numa coluna:codigo TEXT NOT NULL UNIQUE. O erro real éUNIQUE constraint failed: tabela.coluna.- A regra vale no
INSERT, noUPDATEe dentro de um lote (uma linha repetida derruba o lote inteiro). - "Repetido" é "igual para o banco":
'AURORA10'e'aurora10'são diferentes (a não ser comCOLLATE NOCASE), e váriosNULLsão permitidos. UNIQUE (a, b)torna única a combinação; é diferente de umUNIQUEem cada coluna.- Cada
UNIQUEcria um índice (sqlite_autoindex_…). Numa tabela existente, useCREATE UNIQUE INDEX. - Para tratar a repetição sem erro:
INSERT OR IGNOREouON CONFLICT … DO NOTHING / DO UPDATE.