Eu tenho uma coluna: standard BOOLEAN NOT NULL
Gostaria de aplicar uma linha True e todas as outras False. Não há FKs ou qualquer outra coisa, dependendo dessa restrição. Eu sei que posso fazer isso com o plpgsql, mas isso parece uma marreta. Eu preferiria algo como um CHECK
ou UNIQUE
restrição. Quanto mais simples, melhor.
Uma linha deve ser True, nem todas podem ser False (portanto, a primeira linha inserida precisa ser True).
A linha precisará ser atualizada, o que significa que tenho que esperar para verificar as restrições até que as atualizações sejam concluídas, pois todas as linhas podem ser definidas como False primeiro e uma linha True depois.
Existe um FK entre products.tax_rate_id
e tax_rate.id
, mas não tem nada a ver com a taxa de imposto padrão ou padrão, que é selecionável pelo usuário para facilitar a criação de novos produtos.
PostgreSQL 9.5, se for importante.
fundo
A tabela é a taxa de imposto. Uma das taxas de imposto é o padrão ( standard
já que o padrão é um comando do Postgres). Quando um novo produto é adicionado, a taxa de imposto padrão é aplicada ao produto. Se não houver standard
, o banco de dados deve adivinhar ou todos os tipos de verificações desnecessárias. A solução simples, pensei, era garantir que exista um standard
.
Por "padrão" acima, quero dizer para a camada de apresentação (UI). Existe uma opção do usuário para alterar a taxa de imposto padrão. Eu preciso adicionar verificações extras para garantir que a GUI / usuário não tente definir o tax_rate_id como NULL ou apenas defina uma taxa de imposto padrão.
fonte
Respostas:
Variante 1
Como tudo o que você precisa é de uma única coluna
standard = true
, defina o padrão como NULL em todas as outras linhas. Então, umaUNIQUE
restrição simples funciona, pois os valores NULL não a violam:DEFAULT
é um lembrete opcional de que a primeira linha inserida deve se tornar o padrão. Não está forçando nada. Embora não seja possível definir mais de uma linhastandard = true
, você ainda pode definir todas as linhas NULL. Não há uma maneira limpa de evitar isso com apenas restrições em uma única tabela.CHECK
restrições não consideram outras linhas (sem truques sujos).Palavras-chave:
Restrinja dois valores específicos da coluna de existirem ao mesmo tempo
Adicione restrição para tornar a coluna exclusiva por grupo de linhas
Atualizar:
Para permitir um comando como (onde a restrição é satisfeita apenas no final da instrução):
.. a
UNIQUE
restrição teria que serDEFERRABLE
. Vejo:dbfiddle aqui
Variante 2
Tenha uma segunda tabela com uma única linha como:
Crie isso como superusuário:
Agora, há sempre uma única linha apontando para o padrão (neste caso simples, também representando diretamente a taxa padrão). Somente um superusuário poderia quebrá-lo. Você também pode não permitir isso com um gatilho
BEFORE DELETE
.dbfiddle aqui
Palavras-chave:
Você pode adicionar a
VIEW
para ver o mesmo da variante 1 :Nas consultas em que tudo o que você deseja é a taxa padrão, use (somente)
taxrate_standard.taxrate
diretamente.Você adicionou mais tarde:
A implementação de uma pessoa pobre da variante 2 seria apenas adicionar uma linha
products
(ou qualquer tabela semelhante) apontando para a taxa de imposto padrão; um produto fictício que você pode chamar de "Taxa tributária padrão" - se sua configuração permitir.As restrições FK reforçam a integridade referencial. Para concluir, imponha
tax_rate_id IS NOT NULL
a linha (se esse não for o caso da coluna em geral). E não permitir sua exclusão. Ambos podem ser acionados. Nenhuma mesa extra, mas menos elegante e menos confiável.fonte
CROSS JOIN
padrão,LEFT JOIN
o específico e, em seguida,COALESCE
entre os dois.CONSTRAINT standard_only_1_true UNIQUE (standard)
: Suponho que a tabela não será grande, portanto não importa muito, mas como a restrição definirá um índice em toda a tabela, um índice exclusivo parcial nãoWHERE (standard)
utilizará menos espaço?Você pode usar um índice filtrado
dbfiddle aqui
Mas como você disse, a primeira linha deve ser verdadeira, então você pode usar uma restrição CHECK, mas mesmo usando uma função, você pode excluir a primeira linha posteriormente.
dbfiddle aqui
Você pode resolvê-lo adicionando um gatilho BEFORE DELETE para garantir que a primeira linha (foo seja verdadeira) nunca seja excluída.
dbfiddle aqui
fonte