Tenho uma tabela assim:
create table my_table (
id int8 not null,
id_A int8 not null,
id_B int8 not null,
id_C int8 null,
constraint pk_my_table primary key (id),
constraint u_constrainte unique (id_A, id_B, id_C)
);
E eu quero (id_A, id_B, id_C)
ser distinto em qualquer situação. Portanto, as duas inserções a seguir devem resultar em um erro:
INSERT INTO my_table VALUES (1, 1, 2, NULL);
INSERT INTO my_table VALUES (2, 1, 2, NULL);
Mas não se comporta como esperado pois de acordo com a documentação, dois NULL
valores não são comparados entre si, então ambas as inserções passam sem erro.
Como posso garantir minha restrição exclusiva, mesmo que id_C
possa ser NULL
nesse caso? Na verdade, a verdadeira questão é: posso garantir esse tipo de exclusividade em "sql puro" ou tenho que implementá-lo em um nível superior (java no meu caso)?
Postgres 15
Isso funciona fora da caixa com
NULLS NOT DISTINCT
:Ver:
Postgres 14 ou mais antigo (resposta original)
Você pode fazer isso em SQL puro . Crie um índice exclusivo parcial além do que você tem:
Desta forma, você pode inserir
(id_A, id_B, id_C)
na sua tabela:Mas nada disso uma segunda vez.
Ou use dois índices parciais
UNIQUE
e nenhum índice completo (ou restrição). A melhor solução depende dos detalhes de suas necessidades. Comparar:Embora isso seja elegante e eficiente para uma única coluna anulável no
UNIQUE
índice, fica fora de controle rapidamente para mais de uma. Discutindo isso - e como usar o UPSERT com índices parciais:Apartes
Não há uso de identificadores de maiúsculas e minúsculas sem aspas duplas no PostgreSQL.
Você pode considerar uma
serial
coluna como chave primária ou umaIDENTITY
coluna no Postgres 10 ou posterior. Relacionado:Então:
Se você não espera mais de 2 bilhões de linhas (> 2147483647) durante o tempo de vida de sua tabela (incluindo linhas perdidas e excluídas), considere
integer
(4 bytes) em vez debigint
(8 bytes).Eu tive o mesmo problema e encontrei outra maneira de ter NULL exclusivo na tabela.
No meu caso, o campo
foreign_key_field
é um inteiro positivo e nunca será -1.Então, para responder ao Manual Leduc, outra solução poderia ser
Presumo que os ids não sejam -1.
Qual é a vantagem de criar um índice parcial?
Caso você não tenha a cláusula NOT NULL,
id_a
,id_b
eid_c
pode ser NULL juntos apenas uma vez.Com um índice parcial, os 3 campos podem ser NULL mais de uma vez.
Um Null pode significar que o valor não é conhecido para essa linha no momento, mas será adicionado, quando conhecido, no futuro (exemplo
FinishDate
para um runningProject
) ou que nenhum valor pode ser aplicado para essa linha (exemploEscapeVelocity
para um buraco negroStar
).Na minha opinião, geralmente é melhor normalizar as tabelas eliminando todos os Nulls.
No seu caso, você deseja permitir
NULLs
em sua coluna, mas deseja que apenas umaNULL
seja permitida. Por quê? Que tipo de relação é essa entre as duas tabelas?Talvez você possa simplesmente alterar a coluna
NOT NULL
e armazenar, em vez deNULL
, um valor especial (como-1
) que é conhecido por nunca aparecer. Isso resolverá o problema de restrição de exclusividade (mas pode ter outros efeitos colaterais possivelmente indesejados. Por exemplo, usar-1
para significar "não conhecido / não se aplica" distorcerá qualquer soma ou cálculo médio na coluna. Ou todos esses cálculos terão que levar em conta o valor especial e ignorá-lo.)