Eu tenho pesquisado o conceito de ROWGUID recentemente e me deparei com esta questão. Essa resposta deu uma visão, mas me levou a uma toca de coelho diferente com a menção de alterar o valor da chave primária.
Meu entendimento sempre foi que uma chave primária deve ser imutável, e minha pesquisa desde a leitura desta resposta forneceu apenas respostas que refletem o mesmo que uma prática recomendada.
Em que circunstâncias um valor de chave primária precisaria ser alterado após a criação do registro?
Se você estiver usando o nome de uma pessoa como chave primária e seu nome for alterado, você precisará alterar a chave primária. É para isso que
ON UPDATE CASCADE
é usado, uma vez que basicamente transfere a alteração em cascata para todas as tabelas relacionadas que possuem relacionamentos de chave estrangeira com a chave primária.Por exemplo:
A
SELECT
contra ambas as tabelas:Retorna:
Se atualizarmos a
PersonKey
coluna e executarmos novamente oSELECT
:Nós vemos:
Observando o plano da
UPDATE
instrução acima, vemos claramente que ambas as tabelas são atualizadas por uma única instrução de atualização em virtude da chave estrangeira definida comoON UPDATE CASCADE
:Por fim, limparemos nossas tabelas temporárias:
A maneira preferida de fazer isso usando chaves substitutas seria:
Para completar, o plano para a declaração de atualização é muito simples e mostra uma vantagem para chaves substitutas, ou seja, apenas uma única linha precisa ser atualizada em oposição a todas as linhas que contêm a chave em um cenário de chave natural:
A saída das duas
SELECT
declarações acima são:Essencialmente, o resultado é aproximadamente o mesmo. Uma grande diferença é que a chave natural ampla não é repetida em todas as tabelas onde ocorre a chave estrangeira. No meu exemplo, estou usando uma
VARCHAR(200)
coluna para conter o nome da pessoa, o que exige o uso de umVARCHAR(200)
em todos os lugares . Se houver muitas linhas e muitas tabelas contendo a chave estrangeira, isso resultará em muita memória desperdiçada. Observe que não estou falando sobre o desperdício de espaço em disco, pois a maioria das pessoas diz que o espaço em disco é tão barato que é essencialmente gratuito. A memória, porém, é cara e merece ser valorizada. O uso de um número inteiro de 4 bytes para a chave economizará uma grande quantidade de memória quando você considerar o tamanho médio do nome de cerca de 15 caracteres.Tangencial à questão sobre como e por que as chaves podem mudar é a questão sobre por que escolher chaves naturais em vez de chaves substitutas, que é uma questão interessante e talvez mais importante, especialmente onde o desempenho é uma meta de design. Veja minha pergunta aqui sobre isso.
1 - http://weblogs.sqlteam.com/mladenp/archive/2009/10/06/Why-I-prefer-surrogate-keys-instead-of-natural-keys-in.aspx
Embora você possa usar uma chave natural e/ou mutável como seu PK, em minha experiência isso leva a problemas, que muitas vezes podem ser evitados pelo uso de um PK que atenda a estas condições:
Por exemplo, muitas empresas nos EUA tentam usar números de seguridade social como números de identificação pessoal (e PKs) em seus sistemas. Em seguida, eles se deparam com os seguintes problemas - erros de entrada de dados que levam a vários registros que precisam ser reparados, pessoas que não possuem um SSN, pessoas cujo SSN foi alterado pelo governo, pessoas que possuem SSNs duplicados.
Eu já vi cada um desses cenários. Também já vi empresas que não queriam que seus clientes fossem "apenas um número", o que significava que seu PK acabava sendo 'primeiro+meio+último+DOB+zip' ou algo semelhante. Embora eles adicionassem campos suficientes para quase garantir a exclusividade, suas consultas eram horríveis e atualizar qualquer um desses campos significava perseguir problemas de consistência de dados.
Na minha experiência, um PK gerado pelo próprio banco de dados é quase sempre uma solução melhor.
Eu recomendo este artigo para indicações adicionais: http://www.agiledata.org/essays/keys.html
A chave primária pode ser alterada quando a sincronização está envolvida. Este pode ser o caso quando você tem um cliente desconectado e sincroniza os dados com o servidor em determinados intervalos.
Alguns anos atrás, trabalhei em um sistema em que todos os dados de evento na máquina local tinham IDs de linha negativos, como -1, -2 etc. Quando os dados eram sincronizados com o servidor, o ID da linha no servidor era aplicado ao cliente. Digamos que o próximo ID de linha no servidor seja 58. Então -1 se tornaria 58, -2 59 e assim por diante. Essa alteração de ID de linha seria em cascata para todos os registros FK filho na máquina local. O mecanismo também foi usado para determinar quais registros foram sincronizados anteriormente.
Não estou dizendo que esse foi um bom design, mas é um exemplo da mudança da chave primária com o tempo.
Qualquer projeto que envolva mudanças
PRIMARY KEY
regulares é uma receita para o desastre. A única boa razão para alterá-lo seria uma fusão de dois bancos de dados anteriormente separados.Conforme apontado por @MaxVernon, mudanças ocasionais podem ocorrer - então use
ON UPDATE CASCADE
, embora a maioria dos sistemas hoje em dia use um ID como substitutoPRIMARY KEY
.Puristas como Joe Celko e Fabian Pascal (um site que vale a pena seguir) discordam do uso de chaves substitutas, mas acho que eles perderam essa batalha em particular.
Curiosamente, a pergunta vinculada sobre ROWGUID meio que fornece seu próprio caso de uso: quando você tem chaves primárias conflitantes em bancos de dados que precisam ser sincronizados. Se você tiver dois bancos de dados que precisam ser reconciliados e eles usam sequências para chaves primárias, convém que uma das chaves seja alterada para que permaneça exclusiva.
Em um mundo ideal, isso nunca aconteceria. Você usaria GUIDs para as chaves primárias para começar. Realisticamente, porém, você pode nem ter um banco de dados distribuído ao começar a projetar, e convertê-lo em GUIDs pode ter sido um esforço que foi priorizado abaixo da distribuição porque foi considerado de maior impacto do que implementar a atualização de chave. Isso pode acontecer se você tiver uma grande base de código que dependa de chaves inteiras e exija uma revisão importante para converter em GUID. Há também o fato de que GUIDs esparsos (GUIDs que não estão muito próximos uns dos outros, o que acontece se você os gerar aleatoriamente como deveria) também podem causar problemas para certos tipos de índices, o que significa que você deseja evitar o uso como chaves primárias (mencionadas por Byron Jones ).
A estabilidade é uma propriedade desejável para uma chave, mas é algo relativo e não uma regra absoluta. Na prática, muitas vezes é útil alterar os valores das chaves. Em termos relacionais, os dados são identificáveis apenas por suas (super)chaves. Segue-se que, se houver apenas uma chave em uma determinada tabela, a distinção entre A) alterar um valor de chave ou B) substituir o conjunto de linhas em uma tabela por algum conjunto de linhas semelhante ou diferente contendo outros valores de chave é essencialmente uma questão de semântica e não de lógica.
Um exemplo mais interessante é o caso de uma tabela com várias chaves onde os valores de uma ou mais dessas chaves podem ter que mudar em relação a outros valores de chave. Veja o exemplo de uma tabela Employee com duas chaves: LoginName e Badge Number. Aqui está uma linha de amostra dessa tabela:
Se ZoeS perder seu crachá, talvez ela receba um novo e obtenha um novo número de crachá:
Mais tarde, ela pode decidir mudar seu nome de login:
Ambos os valores-chave foram alterados - um em relação ao outro. Observe que não faz necessariamente nenhuma diferença qual deles é considerado "primário".
Na prática, a "imutabilidade", ou seja, nunca alterar um valor, é inatingível ou pelo menos impossível de verificar. Na medida em que a mudança faz alguma diferença, o caminho mais seguro é provavelmente assumir que qualquer chave (ou qualquer atributo) pode precisar ser alterado.
Um cenário possível é, digamos, que você tenha afiliados com ID exclusivo e saiba que eles não serão duplicados entre os afiliados, pois possuem um caráter inicial exclusivo. Os afiliados carregam dados para uma tabela principal. Lá, os registros são processados e, em seguida, atribuídos a um ID mestre. Os usuários precisam acessar os registros assim que são carregados, mesmo que ainda não tenham sido processados. Você deseja que o ID mestre seja baseado no pedido processado e nem sempre processará na ordem em que os registros foram carregados. Eu sei um pouco fabricado.
Imagine uma situação como quando alguém escolheu o National Insurance Number (NIN) como chave primária e, de alguma forma, um operador insere uma linha com o NIN errado. Depois de inserir o valor, existem duas maneiras de corrigir o erro: