Caro leitor, pense comigo: por que os bancos de dados que costumamos usar são chamados de relacionais?
Os bancos são relacionais porque existem relações bem definidas entre as entidades tratadas, ou seja, suas tabelas. Elas, por sua vez, armazenam dados estruturados, que, como sabemos, precisam obrigatoriamente respeitar uma sequência de colunas, tipos de dados, além de outras restrições que venham a ser incluídas nestas tabelas.
Mesmo sendo tudo tão formal e estruturado, tem muita gente que acha que chave estrangeira só serve para causar impacto (negativo) na performance. Honestamente, eu acho que este é um ponto de vista absurdamente limitado.
No meu modo de ver, um banco de dados nunca pode ser visto como um conjunto de tabelas independentes. As tabelas existem por uma necessidade específica (isto é, anormalização dos dados), mas elas nunca funcionam de forma independente.
Na grande maioria dos casos, precisamos juntar dados duas ou mais tabelas para retornar a informação que buscamos. Esta junção de tabelas, obviamente, é feita com base no identificador de registros da tabela mãe (preferencialmente, a chave primária) e o campo equivalente na tabela filha.
Este campo da tabela filha não pode conter valores que não existam no campo identificador da tabela mãe, sob pena de que os registros nesta condição sejam simplesmente perdidos durante a consulta. Ou seja, estes registros da tabela filha nunca serão reconhecidos pela consulta. Eu os chamo de registros fantasma.
Eu trabalhei muito tempo com Data Warehousing e era razoavelmente comum descobrirmos estes registros fantasmas nas tabelas transacionais. E eles precisam ser tratados manualmente (LEIA-SE: CORRIGIDOS) para que serem carregados no DW.
Então, responda: como o seu SGBD garante que estas situações nunca aconteçam? A maneira natural de se fazer isso é criando uma chave estrangeira no campo da tabela filha. Esta chave estrangeira vai fazer uma checagem na tabela mãe antes de inserir ou atualizar o campo de relacionamento da tabela filha. Obviamente que esta checagem terá um certo impacto na performance, mas ela garante qualidade a dos dados!
A chave estrangeira garante também que os campos relacionados das tabelas mãe e filha usem exatamente o mesmo tipo de dados (e com o mesmo comprimento). Você pode até argumentar que existem várias conversões de dados que são feitas automaticamente pelos SGBDs, mas existem problemas que vão além da conversão dos tipos. Por exemplo, o valor “CERTO” (tipo de dados VARCHAR(10)) é diferente de “CERTO ” (CHAR(10)) e obviamente estes registros serão perdidos quando cruzarmos as duas tabelas.
Muitos arquitetos acham uma boa ideia fazer todas estas validações de dados na própria aplicação ao invés de criar uma chave estrangeira na tabela. Esta abordagem parece simples, mas não é. Existe um monte de problemas que podem acontecer durante a operação de um sistema e se a sua aplicação for tratar todos eles sem contar com os recursos do SGBD, certamente vai dar muito trabalho codificar tudo isso.
Ao invés de ser um estorvo, eu vejo as chaves estrangeiras como uma garantia de que a aplicação nunca permitirá que se cadastre dados inválidos nas tabelas do banco. Se não for desta forma, você está passando esta responsabilidade para as equipes que desenvolveram e que irão manter o sistema. Estas equipes passariam a cuidar de uma série de pequenas ações, tais como manipular adequadamente os códigos que são inseridos nas tabelas mãe e filha, criar campos com tipos de dados idênticos e também criar transações que garantam que as inserções/atualizações sejam desfeitas tanto na tabela mãe quanto na tabela filha caso ocorra algum erro.
Eu acho uma péssima ideia transferir para pessoas o que pode ser feito automaticamente por um software destinado a este fim. Por isso, que uso chaves estrangeiras nas tabelas dos meus bancos de dados. Qualidade de dados é coisa séria!