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.

Você vai aprender
  • Como escrever UNIQUE numa coluna, e o erro UNIQUE constraint failed: tabela.coluna.
  • O UNIQUE no INSERT, no UPDATE e num lote de várias linhas.
  • UNIQUE (coluna1, coluna2): quando o que não pode repetir é a combinação.
  • Por que vários NULL passam, e por que 'AURORA10' e 'aurora10' são diferentes (e como mudar isso com COLLATE 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.

ColunaPode repetir?UNIQUE?
Código do cupomNão: cada código é um cupomSim
Email de loginNão: cada email é uma contaSim
Nome do clienteSim: existem dois "José Silva"Não
CidadeSim: muitos clientes por cidadeNã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, coluna codigo.

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.
A Loja Aurora já tem uma regra de par

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:

EscritaQuando 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 NOTHINGO 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 um UNIQUE (ou ser a chave primária).
DO UPDATE SET
"…faça um UPDATE na linha que já existe".
excluded.desconto
excluded ("a excluída") é a linha que o INSERT tentou 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

FormaExemploQuando usar
Na colunacodigo TEXT NOT NULL UNIQUEUma coluna só: a forma mais comum
Restrição de tabelaUNIQUE (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 nomeCONSTRAINT cupom_unico UNIQUE (codigo)Para documentar; no SQLite a mensagem de erro continua mostrando as colunas, não o nome
Índice únicoCREATE UNIQUE INDEX clientes_email_unico ON clientes (email);Tabela que já existe
Sem diferenciar maiúsculascodigo TEXT UNIQUE COLLATE NOCASECódigos e emails, em que A e a são a mesma coisa

Quando dá errado

Cadastrou o mesmo valor duas vezes

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.

A regra no par virou duas regras
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).

O repetido "passou": maiúsculas ou espaços

'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).

O repetido "passou": era 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.

Nos outros bancos

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 NULL passam. No PostgreSQL, dá para mudar isso com UNIQUE NULLS NOT DISTINCT.
  • SQL Server é diferente: numa restrição UNIQUE, a documentação da Microsoft diz que só um NULL é 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

EscritaO que faz
coluna TIPO UNIQUECada 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.cAtualiza a linha existente (upsert)
PRAGMA index_list(tabela);Lista os índices, inclusive os do UNIQUE
Mensagem do SQLiteQuer dizer
UNIQUE constraint failed: cupons.codigoO valor já existe na coluna
UNIQUE constraint failed: avaliacoes.cliente_id, avaliacoes.produto_idO par já existe
Cannot add a UNIQUE columnALTER TABLE … ADD COLUMN … UNIQUE, que o SQLite não aceita
ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraintO 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:

email TEXT NOT NULL

2. O par

Complete a restrição de tabela que impede o mesmo produto duas vezes no mesmo pedido:

(pedido_id, produto_id)

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

  • UNIQUE proíbe valores repetidos numa coluna: codigo TEXT NOT NULL UNIQUE. O erro real é UNIQUE constraint failed: tabela.coluna.
  • A regra vale no INSERT, no UPDATE e 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 com COLLATE NOCASE), e vários NULL são permitidos.
  • UNIQUE (a, b) torna única a combinação; é diferente de um UNIQUE em cada coluna.
  • Cada UNIQUE cria um índice (sqlite_autoindex_…). Numa tabela existente, use CREATE UNIQUE INDEX.
  • Para tratar a repetição sem erro: INSERT OR IGNORE ou ON CONFLICT … DO NOTHING / DO UPDATE.