Mostrando postagens com marcador Modelagem. Mostrar todas as postagens
Mostrando postagens com marcador Modelagem. Mostrar todas as postagens

terça-feira, 6 de novembro de 2012

Chave Primária - "Natural" ou "Surrogate" ?

Sempre que se inicia a modelagem de um sistema, existe (ou pelo menos deveria existir) uma discussão sobre a questão das chaves primárias, baseada na seguinte pergunta: Devemos usar campos com significado de negócio - "Natural Keys" (RG, CPF, Matrícula, Apólice) ou números gerados artificialmente, sem significado fora do sistema - "Surrogate Keys" ?

Já trabalhei com sistemas nas duas abordagens e hoje defendo a segunda opção: chaves artificiais, sem relação alguma com o negócio; apresento aqui os prós e contras que me levaram a essa decisão.

Prós:
  • Chaves artificiais permitem alterações de negócio com menos impacto. Este é provavelmente o argumento mais importante. Chaves naturais estão sempre sujeitas a mudanças. Empresas se fundem, o governo adiciona mais um dígito, a lei muda. Sempre desconfie de um analista que diz que "esse campo não vai mudar nunca". Eu já tive que alterar uma informação (número de apólice) que era PK de uma tabela de 600 mil registros, com mais de 100 "descendentes" distribuídos em 11 níveis de dependência. Acredite, não é legal. Ou você acha impossível que amanhã ou depois o governo brasileiro resolva adicionar um dígito ou dois ao CPF ?
  • Chaves artificiais resultam em menos colunas nas tabelas. A opção das chaves naturais vem sempre acompanhada do uso de chaves compostas, o que leva à redundância da informação e ao desperdício de espaço.
  • Não ocorre o problema das FKs inúteis. Como não há chaves compostas, não existe a possibilidade de um dos campos ser NULL e outro não.
  • Não ocorrem updates nos campos da chave primária, portanto há menos chance de locks problemáticos. Aliás, updates em campos de chave primária violam o modelo relacional bom senso e não deveriam mesmo acontecer em hipótese alguma.
  • Como as chaves são de apenas um campo, e este campo é NUMBER, os joins são mais simples (para o desenvolvedor e para o otimizador) e mais rápidos.
  • O uso de chaves com apenas uma coluna facilita os cálculos para o otimizador de custo (que só sabe mesmo lidar com seletividade de múltiplas colunas no Oracle 11g release 2). Isso resulta em melhores decisões de planos de execução e portanto em melhor desempenho do sistema como um todo.
Contras:
  • Mais operações de join são necessárias para montar a informação para o usuário final. Isso pode ser resolvido de várias maneiras, sendo que "snapshots" ou "materialized views" são a opção mais popular hoje em dia.
  • Uma constraint a mais é necessária (do tipo "unique" nos campos da chave natural). 
  • Parte da integridade de negócio deixa de ser validada pelas FKs. Este é o maior ponto de atenção, pois o banco de dados não detectará anomalias como "pedido de um cliente com itens de outro cliente". Na prática, é difícil uma aplicação estável criar esse tipo de registro, mas processos de carga e migração podem fazê-lo facilmente. 

Veja Também:

O Problema da FK Inútil


Gostaria de esclarecer um comportamento não-intuitivo do Oracle (e de todos os SGBDs aderentes ao padrão ANSI/ISO SQL92), que pode levar a problemas de modelagem de dados e de consistência destes dados.

Imagine uma tabela XPTO, cuja PK é composta de dois campos: ID_XPTO e ID_EMPRESA. Agora imagine outra tabela ACME: (ID_ACME [PK], ID_XPTO [FK] e ID_EMPRESA [FK]). Em ACME, filha de XPTO, ID_XPTO e ID_EMPRESA estão ligados a XPTO por uma constraint do tipo FK, mas permitem valores NULL. Suponha que XPTO tem apenas um registro (1,100) e são inseridas em ACME as seguintes linhas:

1. ACME ( 1, 1, 100 );
2. ACME ( 2, 1, NULL );
3. ACME ( 3, NULL, 100 );
4. ACME ( 4, NULL, NULL );
5. ACME ( 5, 666, NULL );
6. ACME ( 6, NULL, 666 );

Quantas destas linhas serão aceitas ? TODAS ELAS. A presença do valor NULL desobriga o banco da validação da chave estrangeira, portanto devemos tomar o cuidado de adicionar uma constraint de check que garante que ou todos os valores da FK são nulos, ou nenhum é nulo:

ALTER TABLE ACME ADD CONSTRAINT CK_ACME_XXXX CHECK
  ((ID_XPTO IS NULL AND ID_EMPRESA IS NULL) OR (ID_XPTO IS NOT NULL AND ID_EMPRESA IS NOT NULL))

Atenção: Claro que a melhor opção seria deixar os campos em ACME como NOT NULL. A solução acima deve ser utilizada apenas se o requisito de negócio exigir que os campos permitam NULL.