Por que um valor de chave primária mudaria?

18

Eu tenho pesquisado recentemente o conceito de ROWGUID e me deparei com essa pergunta. Essa resposta deu uma ideia, mas me levou a uma toca de coelho diferente com a menção de alterar o valor da chave primária.

Sempre entendi 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 a melhor prática.

Em que circunstâncias um valor da chave primária precisaria ser alterado após a criação do registro?

5crammed
fonte
7
Quando é escolhida uma chave primária que não é imutável?
precisa saber é o seguinte
2
Apenas um pequeno detalhe para todas as respostas abaixo até agora. Alterar um valor na chave primária não é tão importante, a menos que a chave primária também seja o índice em cluster. Realmente importa se os valores do índice clusterizado são alterados.
Kenneth Fisher
6
@KennethFisher ou se for referenciado por um (ou muitos) FKs em outra ou na mesma tabela e uma alteração deve ser conectada em cascata a muitas linhas (possivelmente milhões ou bilhões).
precisa saber é o seguinte
9
Pergunte ao Skype. Quando me inscrevi há vários anos, digitei meu nome de usuário incorretamente (deixei uma carta com meu sobrenome). Tentei várias vezes corrigi-lo, mas eles não puderam alterá-lo porque ele era usado para a chave primária e não suportavam a alteração. Essa é uma instância em que o cliente deseja que a chave primária seja alterada, mas o Skype não deu suporte a isso. Eles poderiam apoiar essa mudança se quisessem (ou pudessem criar um design melhor), mas atualmente não há nada para permitir. Portanto, meu nome de usuário ainda está incorreto.
Aaron Bertrand
3
Todos os valores do mundo real podem mudar (por várias causas). Essa foi uma das motivações originais para chaves substitutas / sintéticas: ser capaz de gerar valores artificiais nos quais se pode confiar para nunca mudar.
usar o seguinte código

Respostas:

24

Se você estivesse usando o nome de uma pessoa como chave primária e o nome dela fosse alterado, seria necessário alterar a chave primária. Isto é o que ON UPDATE CASCADEé usado para uma vez que essencialmente cascatas a descer mudança a todas as tabelas relacionadas que têm relações-chave estrangeira para a chave primária.

Por exemplo:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonKey)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonKey, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonAKAKey, PersonKey)
VALUES ('Death', 'Joe Black');

A SELECTem ambas as tabelas:

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

Devoluções:

insira a descrição da imagem aqui

Se atualizarmos a PersonKeycoluna e executar novamente o SELECT:

UPDATE dbo.People
SET PersonKey = 'Mr Joe Black'
WHERE PersonKey = 'Joe Black';

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

Nós vemos:

insira a descrição da imagem aqui

Observando o plano da UPDATEinstruçã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 como ON UPDATE CASCADE:

insira a descrição da imagem aqui clique na imagem acima para vê-la com mais clareza

Por fim, limparemos nossas tabelas temporárias:

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

O preferido 1 maneira de fazer isso usando chaves substitutas seria:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , PersonName VARCHAR(200) NOT NULL
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonAKAName VARCHAR(200) NOT NULL
    , PersonID INT NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonID)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonName, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonID, PersonAKAName)
VALUES (1, 'Death');

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

UPDATE dbo.People
SET PersonName = 'Mr Joe Black'
WHERE PersonID = 1;

Para completar, o plano da instrução de atualização é muito simples e mostra uma vantagem em substituir as chaves, 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:

insira a descrição da imagem aqui

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

A saída das duas SELECTinstruções acima são:

insira a descrição da imagem aqui

Essencialmente, o resultado é aproximadamente o mesmo. Uma grande diferença é que a chave natural ampla não é repetida em todas as tabelas em que a chave estrangeira ocorre. No meu exemplo, estou usando uma VARCHAR(200)coluna para conter o nome da pessoa, o que exige o uso de um em VARCHAR(200) qualquer lugar . Se houver muitas linhas e muitas tabelas contendo a chave estrangeira, isso adicionará muita memória desperdiçada. Observe que não estou falando de desperdício de espaço em disco, já que a maioria das pessoas diz que o espaço em disco é tão barato que é essencialmente gratuito. A memória, no entanto, é 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 em torno de 15 caracteres.

Importante para a pergunta sobre como e por que as chaves podem mudar é a pergunta sobre por que escolher chaves naturais em vez de chaves substitutas, que é uma pergunta interessante e talvez mais importante, especialmente onde o desempenho é um objetivo do projeto. 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

Max Vernon
fonte
3
Para evitar o CASCADE (que apresenta problemas em determinados cenários), você também pode tornar as colunas FK anuláveis; portanto, se precisar alterar a PK, você pode atualizar as linhas relacionadas para NULL (em partes, se houver muitas ou por tabela) , se houver muitas tabelas ou ambas) e, em seguida, altere o valor de PK e altere os FKs novamente.
Aaron Bertrand
8

Embora você possa usar uma chave que seja natural e / ou mutável como sua PK, na minha experiência isso leva a problemas, que geralmente podem ser evitados pelo uso de uma PK que atenda a essas condições:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

Por exemplo, muitas empresas nos EUA tentam usar os 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 é alterado pelo governo, pessoas que possuem SSNs duplicados.

Eu já vi todos esses cenários. Também vi empresas que não queriam que seus clientes fossem "apenas um número", o que significava que seu PK acabou sendo 'primeiro + meio + último + DOB + zip' ou algo parecido. Embora eles adicionassem campos suficientes para quase garantir a exclusividade, suas consultas eram horríveis e a atualização de qualquer um desses campos significava procurar 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 obter dicas adicionais: http://www.agiledata.org/essays/keys.html

Byron Jones
fonte
6
Um bom conselho do artigo de Scott Ambler mencionado em sua resposta: "Algumas pessoas lhe dizem que você sempre deve usar chaves naturais e outras dizem que você sempre deve usar chaves substitutas. Essas pessoas invariavelmente provam estar erradas, geralmente eles estão fazendo pouco mais do que compartilhar os preconceitos de sua "religião de dados" com você. A realidade é que as chaves naturais e substitutas têm vantagens e desvantagens e que nenhuma estratégia é perfeita para todas as situações ".
Nvogel
7

A chave primária pode ser alterada quando a sincronização está envolvida. Esse pode ser o caso quando você tem um cliente desconectado e ele sincroniza os dados com o servidor em determinados intervalos.

Há alguns anos, trabalhei em um sistema em que todos os dados de eventos na máquina local tinham IDs de linha negativos, como -1, -2 etc. Quando os dados foram sincronizados com o servidor, o ID da linha no servidor foi aplicado ao cliente. Digamos que o ID da próxima linha no servidor seja 58. Então -1 se tornaria 58, -2 59 e assim por diante. Essa alteração do ID da linha seria conectada em cascata a todos os registros filho do FK 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 chave primária sendo alterada ao longo do tempo.

Jon Raynor
fonte
5

Qualquer projeto que envolva mudanças PRIMARY KEYregulares é uma receita para o desastre. A única boa razão para alterá-lo seria uma fusão de dois bancos de dados separados anteriormente.

Conforme apontado pelo @MaxVernon, mudanças ocasionais podem ocorrer - e use-as ON UPDATE CASCADE, embora a maioria dos sistemas hoje em dia use um ID como substituto PRIMARY 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.

Vérace
fonte
3

A estabilidade é uma propriedade desejável para uma chave, mas é uma coisa relativa 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. Daqui resulta 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 um conjunto de linhas semelhante ou diferente contendo outros valores de chave é essencialmente uma questão de semântica ao invés de lógica.

Um exemplo mais interessante é o caso de uma tabela com várias chaves em que os valores de uma ou mais dessas chaves talvez precisem 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:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |47832   |
+---------+--------+

Se o ZoeS perder seu crachá, talvez ela receba um novo e obtenha um novo número de crachá:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |50282   |
+---------+--------+

Mais tarde, ela pode decidir alterar seu nome de login:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZSmith   |50282   |
+---------+--------+

Os dois valores principais foram alterados - em relação um ao outro. Observe que não faz necessariamente nenhuma diferença qual deles é considerado "primário".

Na prática, a "imutabilidade", ou seja, absolutamente nunca alterando 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 supor que qualquer chave (ou qualquer atributo) possa precisar ser alterada.

nvogel
fonte
Eu diminuí o voto do seu comentário devido à seguinte declaração: "Na prática" imutabilidade ", ou seja, absolutamente nunca alterando um valor, é inatingível ou pelo menos impossível de verificar." A imutabilidade é possível e é um dos motivos mais importantes para usar chaves substitutas.
Byron Jones
3
Como você pode saber que alguém não alterará um valor-chave na próxima semana ou daqui a 10 anos? Você pode presumir que não, mas não pode impedir realisticamente que isso aconteça (se você estiver no comando único, poderá criar barreiras para manter todos os demais em perpetuidade, suponho, mas isso parece um caso extremo). O que realmente importa é que as mudanças são muito raras, não que elas nunca ocorram.
Nvogel
3

Curiosamente, a pergunta vinculada sobre o tipo ROWGUID fornece seu próprio caso de uso: quando você tem chaves primárias conflitantes nos bancos de dados que precisam ser sincronizados. Se você tiver dois bancos de dados que precisam ser reconciliados e usarem sequências para chaves primárias, desejará que uma das chaves mude para que permaneça única.

Em um mundo ideal, isso nunca aconteceria. Você usaria GUIDs para as chaves primárias, para começar. Realisticamente, porém, talvez você nem tenha 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, tornando-o distribuído porque foi considerado de maior impacto do que a implementação da atualização principal. Isso pode acontecer se você tiver uma grande base de códigos que depende de chaves inteiras e exigiria uma revisão importante para converter em GUID. Há também o fato de que GUIDs esparsos (GUIDs que não são muito próximos um do outro, 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 ).

jpmc26
fonte
0

Um cenário possível é, digamos, que você tem afiliados com ID exclusivo e sabe que eles não serão duplicados entre afiliados, pois possuem um caractere inicial exclusivo. Os afiliados carregam dados em uma tabela mestre. Os registros são processados ​​e, em seguida, são atribuídos um ID mestre. Os usuários precisam acessar os registros assim que carregados, mesmo que ainda não tenham sido processados. Você deseja que o ID mestre seja baseado no pedido processado e nem sempre processará no pedido em que os registros foram carregados. Eu sei um pouco fabricado.

paparazzo
fonte
-1

Imagine uma situação como quando alguém escolheu o NIN (National Insurance Number) como chave primária e, de alguma forma, um operador insere uma linha com o NIN errado. Após inserir o valor, há duas maneiras de corrigir o erro:

  1. Exclua o registro incorreto e insira um novo
  2. Atualize o valor para o correto e use On Update Cascade se houver uma restrição de integridade referencial nessa coluna
Behrouz Sameny
fonte