Eu tenho uma tabela com este layout:
CREATE TABLE Favorites
(
FavoriteId uuid NOT NULL PRIMARY KEY,
UserId uuid NOT NULL,
RecipeId uuid NOT NULL,
MenuId uuid
)
Quero criar uma restrição única semelhante a esta:
ALTER TABLE Favorites
ADD CONSTRAINT Favorites_UniqueFavorite UNIQUE(UserId, MenuId, RecipeId);
No entanto, isso permitirá várias linhas com o mesmo (UserId, RecipeId)
, se MenuId IS NULL
. Quero permitir que NULL
em MenuId
armazenar um favorito que tem de menu não associado, mas eu só quero, no máximo, uma dessas linhas por par usuário / receita.
As idéias que tenho até agora são:
Use algum UUID codificado (como todos os zeros) em vez de nulo.
No entanto,MenuId
há uma restrição de FK nos menus de cada usuário, então eu teria que criar um menu "nulo" especial para cada usuário que seja um aborrecimento.Verifique a existência de uma entrada nula usando um gatilho.
Eu acho que isso é um aborrecimento e eu gosto de evitar gatilhos sempre que possível. Além disso, não confio neles para garantir que meus dados nunca estejam em mau estado.Apenas esqueça e verifique a existência anterior de uma entrada nula no middleware ou em uma função de inserção e não tenha essa restrição.
Estou usando o Postgres 9.0.
Existe algum método que estou ignorando?
fonte
UserId
,RecipeId
), seMenuId IS NULL
?Respostas:
Crie dois índices parciais :
Dessa forma, só pode haver uma combinação de
(user_id, recipe_id)
ondemenu_id IS NULL
, implementando efetivamente a restrição desejada.Possíveis desvantagens: você não pode ter uma referência de chave estrangeira
(user_id, menu_id, recipe_id)
, não pode basear-seCLUSTER
em um índice parcial e consultas sem umaWHERE
condição correspondente não podem usar o índice parcial. (Parece improvável que você queira uma referência ao FK com três colunas de largura - use a coluna PK).Se você precisar de um índice completo , poderá eliminar a
WHERE
condiçãofavo_3col_uni_idx
ou seus requisitos ainda serão aplicados.O índice, agora composto por toda a tabela, se sobrepõe à outra e fica maior. Dependendo das consultas típicas e da porcentagem de
NULL
valores, isso pode ou não ser útil. Em situações extremas, pode até ajudar a manter os três índices (os dois parciais e o total no topo).Além: aconselho a não usar identificadores de casos mistos no PostgreSQL .
fonte
Você pode criar um índice exclusivo com uma coalescência no MenuId:
Você só precisa escolher um UUID para o COALESCE que nunca ocorrerá na "vida real". Você provavelmente nunca veria um UUID zero na vida real, mas poderá adicionar uma restrição CHECK se for paranóico (e já que eles realmente querem buscá-lo ...):
fonte
(MenuId <> '00000000-0000-0000-0000-000000000000')
embora.NULL
é permitido por padrão. Aliás, existem três tipos de pessoas. Os paranóicos e as pessoas que não fazem bancos de dados. O terceiro tipo ocasionalmente posta perguntas sobre SO com espanto. ;)Você pode armazenar favoritos sem menu associado em uma tabela separada:
fonte
FavoriteWithoutMenu
primeiro. Nesse caso, basta adicionar um link de menu - caso contrário, crio aFavoriteWithoutMenu
linha primeiro e depois a vinculo a um menu, se necessário. Isso também dificulta a seleção de todos os favoritos em uma consulta: eu teria que fazer algo estranho, como selecionar todos os links de menu primeiro e depois selecionar todos os Favoritos cujos IDs não existem na primeira consulta. Não tenho certeza se gosto disso.NULL MenuId
, insira nesta tabela. Caso contrário, para aFavorites
mesa. Mas consultar, sim, será mais complicado.Eu acho que há um problema semântico aqui. Na minha opinião, um usuário pode ter uma (mas apenas uma ) receita favorita para preparar um menu específico. (O OP tem menu e receita misturados; se eu estiver errado: troque MenuId e RecipeId abaixo) Isso implica que {usuário, menu} deve ser uma chave exclusiva nesta tabela. E deve apontar para exatamente uma receita. Se o usuário não tiver uma receita favorita para este menu específico, nenhuma linha deverá existir para esse par de teclas {usuário, menu}. Além disso: a chave substituta (FaVouRiteId) é supérflua: chaves primárias compostas são perfeitamente válidas para tabelas de mapeamento relacional.
Isso levaria à definição de tabela reduzida:
fonte