Desejo impor que apenas um registro em uma tabela seja considerado o valor "padrão" para outras consultas ou exibições que possam acessar essa tabela.
Basicamente, quero garantir que essa consulta sempre retornará exatamente uma linha:
SELECT ID, Zip
FROM PostalCodes
WHERE isDefault=True
Como eu faria isso no SQL?
Nota: Esta resposta foi dada antes de ficar claro que o solicitante queria algo específico do MySQL. Esta resposta é tendenciosa para o SQL Server.
Aplique uma restrição CHECK à tabela que chama uma UDF e garante que seu valor de retorno seja <= 1. A UDF pode apenas contar as linhas na tabela WHERE
isDefault = TRUE
. Isso garantirá que não haja mais de 1 linha padrão na tabela.Você deve adicionar um bitmap ou índice filtrado
isDefault
na coluna (dependendo da sua plataforma) para garantir que essa UDF seja executada muito rapidamente.Ressalvas
PostalCodes
tabela, no entanto, isso continua sendo uma preocupação de desempenho para as situações em que a atividade baseada em conjunto é provável.Dadas todas essas advertências, recomendo usar a sugestão do gbn de um índice exclusivo filtrado em vez de uma restrição de verificação.
Edit: isso é voltado para o SQL Server antes de conhecermos o MySQL
Existem
45 maneiras de fazer isso: em ordem do mais desejado para o menos desejadoNão sabemos para que RDBMS isso se aplica, embora nem todos possam se aplicar
A melhor maneira é um índice filtrado. Isso usa DRI para manter a exclusividade.
Coluna computada com exclusividade (veja a resposta de Jack Douglas) (adicionada pela edição 2)
Uma visão indexada/materializada que é como um índice filtrado usando DRI
Gatilho (conforme outras respostas)
Verifique a restrição com uma UDF. Isso não é seguro para simultaneidade e isolamento de instantâneo. Veja Um Dois Três Quatro
Observe que o requisito de "um padrão" está na tabela, não no procedimento armazenado. O procedimento armazenado terá os mesmos problemas de simultaneidade que uma restrição de verificação com um udf
Nota: perguntado em TANTAS vezes:
Aqui está um procedimento armazenado (dialeto do MySQL):
Para garantir que sua tabela esteja limpa e que o procedimento armazenado esteja funcionando, supondo que o ID 200 seja o padrão, execute estas etapas:
Em vez de um procedimento armazenado, que tal um Trigger?
Para garantir que sua tabela esteja limpa e que o gatilho esteja funcionando, supondo que o ID 200 seja o padrão, execute estas etapas:
Você pode usar um gatilho para aplicar as regras. Quando uma instrução UPDATE ou INSERT define isDefault como True, o SQL no gatilho pode definir todas as outras linhas como False.
Você terá que considerar outras situações ao aplicar isso. Por exemplo, o que deve acontecer se mais de uma linha e UPDATE ou INSERT definir isDefault como True? Que regras serão aplicadas para evitar quebrar a regra?
Além disso, é possível a condição em que não há padrão? O que acontecerá quando uma atualização definir o isDefault de True para False.
Depois de definir as regras, você pode criá-las no gatilho.
Em seguida, conforme indicado em outra resposta, você deseja aplicar uma restrição de verificação para ajudar a aplicar as regras.
A seguir configura uma tabela e dados de exemplo:
Se você criar um procedimento armazenado para definir o sinalizador IsDefault e remover permissões para alterar a tabela base, poderá impor isso com a consulta abaixo. Isso exigiria uma verificação de tabela a cada vez.
Dependendo do tamanho da tabela, um índice em IsDefault (DESC) pode resultar em uma ou ambas as consultas abaixo, evitando uma verificação completa.
Se você não puder remover permissões da tabela base, precisar impor a integridade por outros meios e estiver usando o SQL2008, poderá usar um índice exclusivo filtrado:
Para o MySQL que não possui índices filtrados, você pode fazer algo como o seguinte. Ele explora o fato de que o MySQL permite vários NULLs em índices únicos e usa uma tabela dedicada e uma chave estrangeira para simular um tipo de dados que só pode ter NULL e 1 como valores.
Com esta solução, em vez de verdadeiro/falso, você usaria apenas 1/NULL.
A única coisa que pode dar errado, eu acho, é se alguém adicionar uma linha à
DefaultFlag
tabela. Acho que isso não é uma grande preocupação e você sempre pode corrigi-lo com permissões se realmente quiser estar seguro.Isso estava essencialmente causando problemas porque você colocava o campo no lugar errado e isso é tudo.
Tenha uma tabela 'Defaults', com PostalCode nela, que contenha o id que você está procurando. Em geral, também é bom nomear suas tabelas de acordo com um registro, então o nome da sua tabela inicial seria melhor chamado 'PostalCode'. Os padrões serão uma tabela de linha única, até que você precise especializar seus padrões em relação a alguma outra configuração (como históricos).
A consulta final que você deseja é a seguinte: