Por que as restrições são aplicadas no banco de dados e não no código?

21

Por que as restrições são aplicadas no banco de dados? Não será mais flexível inseri-lo no código?

Estou lendo um livro para iniciantes sobre a implementação de bancos de dados, então estou perguntando isso como iniciante. Digamos que eu projetei um banco de dados, incluindo este modelo de entidade:

 entity type    |   sub-types
----------------+--------------------------------------------
   Person       |   Employee, Student,       ...
   Student      |   Graduate, Undergraduate, ...
   Employee     |   Teacher,  Administrator, ...

Restrições atuais:

  1. Uma pessoa registrada no sistema pode ser apenas um Estudante ou um Funcionário.
  2. A entidade pessoa requer exclusividade de número social, que presumimos que cada pessoa possua apenas um único (ou seja, uma chave primária suficientemente boa ). (veja nº 1)

Mais tarde, decidimos remover o número 1: se um dia a faculdade decidir que o Teacher(o Employeesubtipo) também pode ser Student, fazendo cursos em seu tempo livre, é muito mais difícil alterar o design do banco de dados, que pode ter milhares, milhões, bilhões, zilhões de entradas em vez de apenas alterar a lógica do código: apenas a parte que não permitia que uma pessoa fosse registrada como estudante e como funcionário.

(É muito improvável, mas não consigo pensar em mais nada no momento. Aparentemente é possível).

Por que nos preocupamos com as regras de negócios no design do banco de dados, e não no código?

Nº 1: Uma nota 7 anos depois, um exemplo da vida real:
eu vi um governo em que, devido a um erro, os SSNs emitidos foram duplicados: várias pessoas, o mesmo SSN. Aqueles que projetaram o banco de dados original definitivamente cometeram o erro de não aplicar essa restrição de exclusividade no banco de dados. (e depois um bug no aplicativo original - vários aplicativos usando o banco de dados compartilhado e não concordando onde colocar, verificar e impor a restrição? ...).
Este bug continuará a viver no sistema e todo o sistema desenvolvido após o qual confiar no banco de dados do sistema original, por muitos e muitos anos. Lendo as respostas aqui, aprendi a aplicar todas as restrições, o maior número possível, sabiamente (não cegamente) no banco de dados para representar o mundo físico real lá fora, o melhor que posso.

hkoosha
fonte
2
Nos preocupamos principalmente com o cumprimento das regras de negócios e qual é a melhor maneira para isso.
precisa saber é o seguinte
3
Na verdade, você está apresentando um exemplo muito ruim de para que restrições são usadas, uma vez que a flexibilidade de suas entidades e a capacidade de expansão do banco de dados são definidas principalmente pela normalização. Dito isto, as restrições são a proteção final contra qualquer dado corrompido que entre no banco de dados, mesmo que o aplicativo esteja com erros, mesmo que um novo aplicativo seja desenvolvido, mesmo que uma API externa seja adicionada, mesmo que alguém edite o banco de dados diretamente. As restrições protegem o banco de dados, além disso, a lógica de negócios também precisará fazer suas próprias coisas antes de tentar acessar o banco de dados.
Niels Keurentjes
3
Na verdade, como estudante de graduação, sou considerado um aluno, funcionário e professor. Portanto, seu exemplo não é realmente improvável.
Winston Ewert
4
Você nunca deve basear um design de banco de dados nos objetos em seu aplicativo. Você normalmente designaria isso como pessoa e, em seguida, teria uma tabela relacionada para definir os papéis das pessoas. Então o problema não surge quando você cria uma tabela para os papéis, para que as pessoas possam ter vários papéis. Se você deseja ter apenas uma pessoa de função, restringe a tabela para que o peopleID seja exclusivo. Quando você quiser alterar isso, remova a restrição.
HLGEM
Objeto <-> O mapeamento relacional é uma arte.
Thorbjørn Ravn Andersen

Respostas:

34

Algumas restrições são melhor aplicadas no banco de dados e outras são melhor aplicadas no aplicativo.

As restrições que são melhor aplicadas no banco de dados geralmente existem porque são fundamentais para a estrutura do modelo de dados, como uma contraint de chave estrangeira para garantir que um produto tenha uma validade category_id.

As restrições impostas em um aplicativo podem não ser fundamentais para o modelo de dados, como todos os produtos FooBar devem ser azuis - mas mais tarde alguém pode decidir que o FooBars também pode ser amarelo. Essa é a lógica do aplicativo que realmente não precisa estar no banco de dados, embora você possa criar uma colourstabela separada e o banco de dados possa exigir que o produto faça referência a uma entrada válida dessa tabela. MAS a decisão de que o único registro em colourstem o valor blueseria ainda vir de algum lugar fora do banco de dados.

Considere o que aconteceria se você não tivesse restrições no banco de dados e exigisse que todas fossem aplicadas no aplicativo. O que aconteceria se você tivesse mais de um aplicativo que precisava trabalhar com os dados? Como seriam seus dados se os diferentes aplicativos decidirem aplicar restrições de maneira diferente?

Seu exemplo mostra uma situação em que poderia ter sido mais benéfico ter a restrição no aplicativo do que no banco de dados, mas talvez houvesse um problema fundamental com o modelo de dados inicial sendo muito restritivo e inflexível?

FrustratedWithFormsDesigner
fonte
Portanto, de acordo com esta resposta, a regra <uma pessoa só pode existir na tabela de subtipos de Alunos ou apenas na tabela de subtipos de Empregados> deve ser aplicada no código E o banco de dados tem <O subtipo de Aluno / Empregado deve ser válido pessoa> restrição. Estou certo? (Foi o exemplo do livro). obrigado.
hkoosha
2
@loolooyyyy: Sim, acho que está correto. Se o banco de dados aplica a primeira regra (que uma pessoa pode ser um aluno ou funcionário), a situação que você descreveu (na qual um funcionário deseja se registrar para uma aula) é impossível porque: a pessoa não pode ser as duas coisas, e não é ainda é possível criar um segundo registro de "pessoa" porque eles não podem compartilhar números de seguridade social que provavelmente são emitidos por terceiros (como o governo). Naturalmente, este modelo de dados excessivamente restritiva pode funcionar para alguns casos ...
FrustratedWithFormsDesigner
2
@loolooyyyy: Outra maneira de usar o modelo de dados original e ainda permitir que os professores sejam alunos pode ser ter outra tabela chamada, teachers_as_studentsque é outro subtipo Studentse que possui uma nova chave estrangeira referente a Teachersuma chave primária gerada pelo sistema , em vez de uma Social Número de segurança. Dessa forma, um "aluno" na verdade é um apelido para um professor, para que ele ainda possa se registrar para assistir a uma aula. É difícil dizer com certeza o quão bem isso funcionaria sem ver todo o modelo de dados.
FrustratedWithFormsDesigner
2
Eu votei contra isso. Não há tempo em que uma restrição é melhor aplicada apenas no aplicativo . O tom desta resposta é ponderado incorretamente.
Evan Carroll
3
@FrustratedWithFormsDesigner certamente, é na verdade o filho do pôster de uma restrição de chave estrangeira. Suponha que você tenha três clientes de diferentes versões / compilações do ponto de acesso db, o que você fará quando parar de enviar o produto em vermelho? Onde você vai armazenar a lista de possíveis combinações de cores? Dica: eu tenho um lugar centralizado para você. E se você criar a tabela color_products, e colorprovavelmente poderá criar as listas suspensas adicionais com mais facilidade - a maioria dos IDEs / carregadores de esquema suporta as seguintes chaves.
Evan Carroll
35

Porque:

  1. Quero que todos os dados no banco de dados estejam sujeitos às mesmas restrições, não apenas os novos dados estejam sujeitos às restrições na versão do código que está sendo executado hoje.
  2. Eu quero restrições declarativas, não restrições programáticas.
  3. Os dados no banco de dados geralmente sobrevivem ao código escrito para interagir com ele hoje. E esses dados - não o código - são o ativo da organização.
  4. Meu código se torna muito mais simples quando eu sei que todos os dados estão sujeitos a restrições rigorosas. Não preciso mais considerar casos especiais que sei que o banco de dados garante ser impossível.

Apenas algumas razões que são importantes para mim.

Colin 't Hart
fonte
4
Semi-relacionado a (1) e (3): erros no código do aplicativo podem ser corrigidos, erros nos seus dados geralmente são irreparáveis.
mu é muito curta
17

Os dados provavelmente sobreviverão por muito tempo ao código do aplicativo. Se a regra for crítica para que os dados sejam úteis ao longo do tempo (como restrições de chave estrangeira que ajudam a manter a integridade dos dados), ela deve estar no banco de dados. Caso contrário, você corre o risco de perder a restrição em um novo aplicativo que atinge o banco de dados. Não apenas vários aplicativos atingem bancos de dados (incluindo alguns que podem não perceber que existe uma regra de dados importante), mas alguns deles, como importações de dados ou aplicativos de relatório, podem não ser capazes de usar a camada de dados configurada no aplicativo de entrada de dados principal. Francamente, as chances de haver um bug na restrição são muito maiores no código do aplicativo na minha experiência.

Na minha opinião pessoal (com base em mais de 30 anos lidando com dados e experiência com centenas de bancos de dados diferentes usados ​​para diversos fins), qualquer pessoa que não coloque as restrições no banco de dados a que pertence, acabará por ter poucos dados. Às vezes, dados ruins a ponto de ficarem inutilizáveis. Isso é especialmente verdade quando você tem dados financeiros / regulatórios que precisam atender a certos critérios de auditoria.

HLGEM
fonte
17

A maioria das restrições de integridade referenciais implementadas fora do banco de dados pode ser derrotada. Portanto, se você deseja que seus dados tenham integridade garantida o tempo todo, é necessário aplicar restrições no banco de dados. Ponto final, é isso.

Normalmente, as restrições no nível do aplicativo são derrotadas pelo mecanismo de consistência de leitura do banco de dados, pelo qual as sessões não podem exibir os dados de outras sessões até que sejam confirmadas.

Por exemplo, duas sessões podem tentar inserir o mesmo valor em uma coluna que se destina a ser exclusiva. Ambos podem verificar ao mesmo tempo que o valor ainda não existe, podem inserir seu valor e podem confirmar. Uma restrição exclusiva implementada no banco de dados não deixaria isso acontecer.

A propósito, isso não é desconhecido para os designers de linguagem de aplicativos. Leia a exclusividade da seção 3.10 nos Guias do Ruby on Rails: validações de registro ativas e retornos de chamada

Esse ajudante valida que o valor do atributo é único antes do objeto ser salvo. Ele não cria uma restrição de exclusividade no banco de dados; portanto, duas conexões diferentes ao banco de dados criam dois registros com o mesmo valor para uma coluna que você pretende ser exclusiva. Para evitar isso, você deve criar um índice exclusivo no seu banco de dados.

David Aldridge
fonte
16

Benefícios das restrições impostas pelo banco de dados:

Simplicidade - declarar uma restrição é significativamente mais simples do que declarar uma restrição e escrever o código que aplicará essa declaração.

Precisão - o código que você não escreveu nunca terá um bug que você criou. Os fornecedores de banco de dados gastam tempo certificando-se de que seu código de restrição é preciso, para que você não precise.

Velocidade - seu aplicativo nunca pode ter mais distribuições do que o banco de dados em que se baseia. Os fornecedores de banco de dados gastam tempo certificando-se de que seu código de restrição seja eficiente, para que você não precise. O próprio banco de dados também tem acesso mais rápido aos dados do que um aplicativo poderia ter, por mais eficiente que fosse.

Reutilização - Você pode começar com um aplicativo em uma plataforma, mas pode não continuar assim. E se você precisar acessar os dados de um SO diferente, hardware diferente ou de uma interface de voz? Por ter restrições no banco de dados, esse código nunca precisa ser reescrito para a nova plataforma e nunca deve ser depurado por precisão ou perfilado por velocidade.

Completude - Os aplicativos impõem restrições quando os dados são inseridos no banco de dados e exigiriam um esforço adicional para verificar se os dados mais antigos são precisos ou para manipular os dados que já estão no banco de dados.

Longevidade - Sua plataforma de banco de dados provavelmente sobreviverá a qualquer aplicativo específico.

Leigh Riffel
fonte
11

Por que as restrições são aplicadas no servidor? Porque você não pode forçar os bandidos a usar seu cliente.

Para esclarecer, se você estiver executando apenas o processamento de regras de negócios em seu aplicativo cliente, alguém usando outra ferramenta poderá se conectar ao servidor de banco de dados e fazer o que quiser, sem ser restringido por nenhuma das regras de negócios e verificações de integridade. Impedir alguém de usar uma ferramenta arbitrária em qualquer lugar da rede é muito difícil.

Se você fizer a verificação de integridade no servidor de banco de dados, todas as tentativas de acessar dados, independentemente da ferramenta, serão restringidas por suas regras.

Andarilho de Pedra Verde
fonte
10

Algumas ótimas respostas aqui e com o risco de repetir outros pensamentos:

  • SSN não é necessariamente único. Caramba, o SSN nem sempre é conhecido e, em alguns casos, ainda não existe. Os SSNs podem ser reutilizados e nem todos os funcionários ou estudantes podem ter um SSN. Isso é periférico à questão, mas demonstra que, não importa onde você imponha suas restrições, você precisa entender o modelo de dados e o domínio com bastante profundidade para tomar decisões sobre as regras de negócios.
  • Pessoalmente, prefiro que as restrições sejam o mais próximas possível dos dados. A razão muito simples é que nem todos usarão o código do aplicativo para alterar os dados no banco de dados. Se você aplica suas regras de negócios no nível do aplicativo e eu executo uma UPDATEinstrução diretamente no banco de dados, como seu aplicativo impede uma alteração inválida? Outro problema com as regras de negócios no aplicativo é que a recompilação / reimplantação pode ser difícil, especialmente para aplicativos distribuídos, onde é possível que nem todos recebam a atualização ao mesmo tempo. E, finalmente, alterar as regras de negócios no aplicativo não faz absolutamente nada sobre os dados que já existem que violam as novas regras - se você adicionar a nova restrição aos dados, precisará corrigi-los.
  • Você pode justificar várias verificações redundantes em vários níveis. Tudo isso depende da flexibilidade das metodologias de implantação, da probabilidade de uma mudança e da dificuldade de sincronizar uma alteração de regra de negócios no banco de dados e em outras camadas. Um argumento convincente para repetir verificações na camada de aplicativo é que você pode impedir uma ida e volta ao banco de dados apenas para falhar em uma restrição (dependendo da natureza da restrição e se ela depende dos dados existentes). Mas se eu tivesse que escolher um ou outro, o colocaria no banco de dados pelos motivos acima.

No caso de você mencionar explicitamente, onde de repente você está permitindo algo que não era permitido anteriormente, isso não é realmente um problema - você remove qualquer restrição imposta, independentemente de onde isso exista. No caso oposto, onde de repente os professores não podem mais ser alunos, você potencialmente tem um monte de dados para limpar, novamente independentemente de onde a restrição existia anteriormente.

Aaron Bertrand
fonte
9
  1. O banco de dados pode verificar as restrições efetivamente. Melhor que o código.

  2. As restrições de integridade ajudam o banco de dados a encontrar um plano de execução eficaz

  3. O aplicativo vê uma visualização consistente da leitura, portanto, dificilmente pode garantir exclusividade. Embora o banco de dados também possa ver dados não confirmados.

ibre5041
fonte
8

Resposta curta ... para preservar a integridade dos dados (ou seja, precisão e validade).

Uma exceção ...
Se o banco de dados estiver apenas armazenando os dados de um único aplicativo para um único usuário, como na maioria dos bancos de dados Sqlite, ele pode não precisar de restrições. Na verdade, eles geralmente não o fazem, para manter o tempo de acesso tão rápido que é incomensurável.

Para todo o resto ... Os
bancos de dados sempre atendem a dois mestres que chamarei de editores e usuários .

Os editores geralmente colocam dados no banco de dados e recuperam dados um ou um pequeno número de registros por vez. Suas principais preocupações são o acesso rápido e preciso a todos os dados relacionados e o armazenamento rápido e confiável de suas alterações.

Os usuários geralmente recuperam dados e estão mais preocupados com o acesso rápido a informações inquestionavelmente precisas. Eles geralmente precisam de várias contagens, agregações e listagens que costumavam ser geradas naquelas pilhas icônicas de impressões em papel greenbar, mas geralmente acabam nas páginas da web hoje.

Os projetos de desenvolvimento de banco de dados quase sempre são iniciados por ordem dos Usuários , mas o design é orientado pelas necessidades de entrada de dados e registro por vez dos Editores . Dessa forma, desenvolvedores inexperientes geralmente respondem à necessidade imediata de velocidade (principalmente do desenvolvimento ), não colocando restrições no banco de dados.

Se um e apenas um aplicativo for usado para fazer alterações nos dados por toda a vida útil do banco de dados, e esse aplicativo for desenvolvido por um ou um pequeno número de indivíduos bem coordenados, pode ser razoável contar com o aplicativo para garantir a integridade dos dados.

No entanto, por mais que fingimos que podemos prever o futuro, não podemos.

O esforço para produzir qualquer banco de dados é valioso demais para ser descartado. Como uma casa, o banco de dados será estendido, alterado e renovado várias vezes. Mesmo quando for completamente substituído, todos os dados serão migrados para o novo banco de dados, preservando todas as regras e relacionamentos de negócios antigos.

As restrições implementam essas regras e relacionamentos de forma declarativa e concisa no próprio mecanismo de banco de dados, onde são facilmente acessados. Sem eles, os desenvolvedores subsequentes precisariam passar pelos programas aplicativos para fazer engenharia reversa dessas regras. Boa sorte!

A propósito, é exatamente isso que os programadores COBOL de mainframe precisam fazer, pois esses bancos de dados maciços costumavam ser criados antes de termos restrições e mecanismos relacionais. Mesmo se migrados para um sistema moderno como o DB2 da IBM, as restrições às vezes não são totalmente implementadas, já que a lógica das regras antigas, incorporadas talvez em uma série de programas "batch" de COBOL, pode ser tão complicada que não é prática a conversão. Em vez disso, ferramentas automatizadas podem ser usadas para converter o antigo COBOL em uma versão mais recente, com interfaces para o novo mecanismo relacional e com um pouco de ajustes, a integridade dos dados é preservada ... até que um novo aplicativo seja escrito que corrompa sutilmente tudo e a empresa seja transportada no tribunal por, por exemplo, excluir milhares de proprietários de casas que eles não deveriam ter.

DocSalvager
fonte
7

Além dos outros comentários ...

Se / quando você tiver um banco de dados em que qualquer tabela possa ser atualizada por um ou mais aplicativos ou caminhos de código, a colocação das restrições apropriadas no banco de dados significa que seus aplicativos não duplicarão o "mesmo" código de restrição. Isso beneficia você simplificando a manutenção (reduzindo o número de locais a serem alterados se / quando houver uma alteração no modelo de dados) e garante que as restrições sejam aplicadas de forma consistente, independentemente do aplicativo que atualiza os dados.

gsiems
fonte
5

Pessoalmente, acho que é mais fácil criar e alterar restrições do que criar gatilhos, por exemplo, que seria uma maneira de impor sua regra de negócios usando o código-fonte.

Também é menos provável que os acionadores sejam portáteis, pois geralmente são escritos em linguagens específicas do fornecedor, como PL / SQL.

Mas se as restrições não atenderem às suas necessidades, você sempre poderá usar gatilhos para impor suas regras de negócios.

Pérola
fonte
5
Os gatilhos também não garantem a integridade, devido a problemas de consistência de leitura.
precisa
3

Eles sempre devem ser aplicados no banco de dados primeiro porque,

  1. O banco de dados garante integridade entre diferentes clientes. Você pode ter clientes diferentes em plataformas diferentes acessando o banco de dados. As restrições no banco de dados não arriscam problemas de integridade quando você cria um novo cliente. Isso evita que você tenha que responder suas restrições no caso de uma reescrita ou de um ponto de acesso adicional.
  2. O banco de dados possui uma DSL para criar restrições: SQL DDL!
  3. O banco de dados fornece acesso a essas restrições nos catálogos do sistema para que um ORM ou "carregador de esquema" adequado possa ler essas restrições e trazê-las para o seu aplicativo. Por exemplo, se seu banco de dados especificar que você tem um varchar(5)tipo, há uma boa chance de encontrar um esquema carregando ORM para seu idioma específico que mapeia o tipo de idioma para o tipo do esquema e monta sua própria restrição de tamanho. DBIx for Perl is one such schema loader; aqui está outro para o Entity Framework . As habilidades desses carregadores variam, mas tudo o que eles podem fornecer é um bom começo para garantir a integridade do aplicativo sem a necessidade de ir ao banco de dados.
Evan Carroll
fonte