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.

terça-feira, 9 de outubro de 2012

Novas Features do Oracle 12c

Estive na última quinta-feira na famosa palestra que Tom Kyte (asktom.oracle.com) faz a cada lançamento de versão do banco de dados Oracle ("My X favorite things about Oracle X"), agora na versão 12c. Foi uma apresentação bem corrida, mas reproduzo aqui as novidades que achei mais interessantes. Deixo de fora a maior de todas elas, "Pluggable Databases", que terá um post exclusivo no futuro.

Adaptive Execution Plans


Hoje em dia, o plano de execução de um SQL qualquer é definido antes da execução, com base nas estatísticas. Se estas estiverem desatualizadas ou erradas, um plano de execução ruim será gerado, e a execução consumirá mais recursos do que o necessário.

No Oracle 12c, o otimizador será capaz de detectar más escolhas durante a execução, e mudar o plano ao invés de insistir no erro. Se, por exemplo, as estatísticas indicavam que a tabela EMP tinha 10 registros mas na hora da execução ele perceber que eram 10 milhões, ele poderá decidir mudar a operação de NESTED LOOPS para um HASH JOIN.

Sim, eu sei o que você está pensando: e quando houver um problema de desempenho, como eu vou saber qual foi o plano utilizado ? Essa informação sairá apenas em traces, então vamos torcer para que o otimizador decida corretamente.

Enhanced Statistics


Diversas melhorias relacionadas à coleta e utilização de estatísticas:

  • A coleta dinâmica (dynamic sampling) agora pode ser configurada com o valor 11, o que faz com que a coleta seja persisitida no banco. Não ficou claro se essa persistência será no mesmo local das estatísticas tradicionais, coletadas com DBMS_STATS.
  • Temos agora um novo tipo de histograma (hybrid) que é utilizado automaticamente em colunas com mais de 254 valores distintos.
  • Estatísticas agora serão coletadas automaticamente durante cargas de dados.
  • Para tabelas temporárias (Global Temporary Tables) as estatísticas serão privadas para cada sessão de usuário.

Top-N and Pagination Queries


Agora será possível em Oracle solicitar diretamente em SQL "os primeiros 10 registros" ou "os próximos 20 registros" sem necessidade do velho truque "subselect + order by + rownum".

Improved Defaults


Temos diversas melhorias relacionadas a valores default:

  • Default to Sequence: o valor default agora pode ser o NEXTVAL de uma sequence.
  • Default to Null Inserted: este valor default será utilizado mesmo que seja especificado NULL durante o insert. 
  • Metadata Defaults: a adição de uma nova coluna com um valor default passa a ser instantânea, para qualquer tamanho de tabela.

Temporary Undo


Hoje, por estranho que possa parecer, operações temporárias geram REDO. Isso é porque elas precisam gerar UNDO para fins de recovery, e essa geração de UNDO gera REDO. Na versão 12c, o parâmetro TEMP_UNDO_ENABLED faz com que a geração de UNDO das operações temporárias seja também uma operação temporária, eliminando a geração de REDO. Isso deve causar melhorias espontâneas de desempenho em diversas aplicações pelo mundo afora.

Increased Limits for Varchar


O limite de tamanho para colunas VARCHAR2, NVARCHAR2 ou RAW agora é de 32K. Este já era o tamanho permitido para os tipos de dado homônimos em PL/SQL.