Quero 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 retorne exatamente uma linha:
SELECT ID, Zip
FROM PostalCodes
WHERE isDefault=True
Como eu faria isso no SQL?
mysql
database-design
emaynard
fonte
fonte
PostalCodes
está vazio? Se uma linha já tiver uma propriedade, deve ser impedida de ser definida como falsa, a menos que outra linha (se houver) seja definida como verdadeira na mesma instrução SQL? Zero linhas podem ter a propriedade entre os limites da transação? A última linha da tabela deve ser forçada a ter a propriedade e ser impedida de ser excluída? A experiência me diz que "garantir exatamente uma linha" tende a significar algo diferente na realidade, geralmente simplesmente "no máximo uma linha".Respostas:
Edit: isso é voltado para o SQL Server antes de conhecermos o MySQL
Existem
4maneiras de fazer isso: na ordem do mais desejado para o menos desejadoNós não sabemos o que RDBMS isso se, embora para que nem todos possam se aplicar
A melhor maneira é um índice filtrado. Isso usa o DRI para manter a exclusividade.
Coluna computada com exclusividade (consulte a resposta de Jack Douglas) (adicionada pela edição 2)
Uma exibição indexada / materializada que é como um índice filtrado usando DRI
Disparador (conforme outras respostas)
Verifique a restrição com um UDF. Isso não é seguro para o isolamento de simultaneidade e instantâneo. Veja um dois três quatro
Observe que o requisito para "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: solicitado em SO muitas vezes:
fonte
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 um UDF e verifique se seu valor de retorno é <= 1. O UDF pode apenas contar as linhas na tabela WHERE
isDefault = TRUE
. Isso garantirá que não haja mais de uma linha padrão na tabela.Você deve adicionar um bitmap ou índice filtrado na
isDefault
coluna (dependendo do seu platforom) para garantir que esse UDF seja executado rapidamente.Ressalvas
PostalCodes
tabela, no entanto, isso continua sendo uma preocupação de desempenho para as situações em que é provável uma atividade baseada em conjunto.Dadas todas essas advertências, recomendo usar a sugestão de gbn de um índice exclusivo filtrado em vez de uma restrição de verificação.
fonte
Aqui está um procedimento armazenado (MySQL Dialect):
Para garantir que sua tabela esteja limpa e 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 gatilho?
Para garantir que sua tabela esteja limpa e o gatilho esteja funcionando, supondo que o ID 200 seja o padrão, execute estas etapas:
fonte
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 definiremDefault como True? Quais regras serão aplicadas para evitar violar 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 construí-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.
fonte
A seguir, configure 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á aplicá-lo com a consulta abaixo. Exigiria uma varredura 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 conseguir remover as permissões da tabela base, precisar impor a integridade por outros meios e estiver usando o SQL2008, poderá usar um índice exclusivo filtrado:
fonte
Para o MySQL que não possui índices filtrados, você pode fazer algo como o seguinte. Ele explora o fato de o MySQL permitir vários NULLs em índices exclusivos 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.fonte
Isso estava essencialmente lhe dando problemas, porque você colocou o campo no lugar errado e isso é tudo.
Tenha uma tabela 'Padrões', com PostalCode, que contém o ID que você está procurando. Em geral, também é bom nomear suas tabelas de acordo com um registro, para que o nome da sua tabela inicial seja melhor chamado 'PostalCode'. Os padrões serão uma tabela de linha única, até que você precise especializar seus padrões em alguma outra configuração (como históricos).
A consulta final que você deseja é a seguinte:
fonte