chaves primárias compostas é uma má prática? [fechadas]

14

Quero saber se as chaves primárias compostas são uma prática ruim e, se não, em quais cenários é recomendável usar.

Minha pergunta é baseada neste artigo

erros de design de bancos de dados

A parte sobre chaves primárias compostas:

Má prática nº 6: chaves primárias compostas

Esse é um ponto controverso, já que muitos projetistas de bancos de dados falam hoje em dia sobre o uso de um campo gerado automaticamente de ID de número inteiro como chave primária, em vez de um campo composto definido pela combinação de dois ou mais campos. Atualmente, isso é definido como a “melhor prática” e, pessoalmente, eu tendem a concordar com isso.

Imagem de uma chave primária composta

No entanto, isso é apenas uma convenção e, é claro, os DBEs permitem a definição de chaves primárias compostas, que muitos designers consideram inevitáveis. Portanto, como na redundância, as chaves primárias compostas são uma decisão de design.

Cuidado, no entanto, se é esperado que sua tabela com uma chave primária composta tenha milhões de linhas, o índice que controla a chave composta pode crescer até um ponto em que o desempenho da operação CRUD é muito reduzido. Nesse caso, é muito melhor usar uma chave primária de ID inteiro simples, cujo índice será compacto o suficiente e estabelecer as restrições DBE necessárias para manter a exclusividade.

hackvan
fonte
4
Esta não é uma prática "boa" ou "ruim". Toda decisão de projeto deve servir a um propósito; se você puder explicar (a si e aos outros) por que precisa de uma PK composta, está pronto. Por outro lado, se você puder explicar por que não precisa, também estará pronto. O artigo ao qual você vincula faz um trabalho muito ruim, explicando, na minha opinião.
21417 mustaccio
este artigo indica um ponto, mas se observarmos estruturas populares (como trilhos, por exemplo) em suas "melhores práticas" não suportam esse tipo de chave primária, então perguntei por quê? é por dificuldades técnicas ou algo mais.
hackvan
É mais fácil para os designs de estrutura suportar apenas chaves primárias inteiras de coluna única "simples". E como a maioria dos desenvolvedores (pelo menos na minha experiência pessoal) não tem muito em termos de habilidades no banco de dados (em relação aos usuários deste site, pelo menos), ele funciona bem o suficiente para a maioria dos usuários do software. Como a maioria dos usuários do software não precisa de chaves compostas (ou acha que elas precisam, pelo menos no início), eles podem se safar por não fornecer (bom) suporte para chaves compostas.
Willem Renzema
11
Como um GUID é melhor que um INTEGER [Serial | Auto_Increment | Identidade <whatever_integer_you_like>]?
Vérace 21/10
4
Eu não iria contratar esse autor
paparazzo

Respostas:

31

Dizer que o uso de "Composite keys as PRIMARY KEY is bad practice"é um total absurdo!

Os compostos PRIMARY KEYsão frequentemente uma "coisa boa" e a única maneira de modelar situações naturais que ocorrem na vida cotidiana!

Pense no exemplo clássico de ensino de bancos de dados-101 de estudantes e cursos e nos muitos cursos realizados por muitos estudantes!

Crie tabelas de curso e aluno:

CREATE TABLE course
(
  course_id SERIAL,
  course_year SMALLINT NOT NULL,
  course_name VARCHAR (100) NOT NULL,
  CONSTRAINT course_pk PRIMARY KEY (course_id)
);


CREATE TABLE student
(
  student_id SERIAL,
  student_name VARCHAR (50),
  CONSTRAINT student_pk PRIMARY KEY (student_id)
);

Vou dar o exemplo no dialeto do PostgreSQL (e MySQL ) - deve funcionar para qualquer servidor com alguns ajustes.

Agora, você obviamente quer manter o controle de quais estudante está tomando qual curso - então você tem o que é chamado um joining table(também chamados linking, many-to-manyou m-to-ntabelas). Eles também são conhecidos como associative entitiesno jargão mais técnico!

Um curso pode ter muitos alunos.
1 aluno pode fazer muitos cursos.

Então, você cria uma tabela de junção

CREATE TABLE course_student
(
  cs_course_id INTEGER NOT NULL,
  cs_student_id INTEGER NOT NULL,

  -- now for FK constraints - have to ensure that the student
  -- actually exists, ditto for the course.

  CREATE CONSTRAINT cs_course_fk FOREIGN KEY (cs_course_id) REFERENCES course (course_id),
  CREATE CONSTRAINT cs_student_fk FOREIGN KEY (cs_student_id) REFERENCES student (student_id)
);

Agora, a única maneira de dar uma sensata a esta tabela PRIMARY KEYé fazer disso KEYuma combinação de curso e aluno. Dessa forma, você não pode obter:

  • uma duplicata da combinação de alunos e cursos

    • um curso só pode ter o mesmo aluno matriculado uma vez e

    • um aluno só pode se inscrever no mesmo curso apenas uma vez

  • você também tem uma pesquisa pronta KEYno curso por aluno - também conhecido como índice de cobertura ,

  • é trivial encontrar cursos sem alunos e estudantes que não estão fazendo nenhum curso!

    - O exemplo db-fiddle possui a restrição PK dobrada na CREATE TABLE - Isso pode ser feito de qualquer maneira. Prefiro ter tudo na instrução CREATE TABLE.


ALTER TABLE course_student 
ADD CONSTRAINT course_student_pk 
PRIMARY KEY (cs_course_id, cs_student_id);

Agora, você poderia, se estivesse achando que as pesquisas por aluno por curso eram lentas, use UNIQUE INDEXon (sc_student_id, sc_course_id).

ALTER TABLE course_student 
ADD CONSTRAINT course_student_sc_uq  
UNIQUE (cs_student_id, cs_course_id);

Não é nenhuma bala de prata para a adição de índices - que vai fazer INSERTs e UPDATEmais lento, mas o grande benefício da enorme decrescentesSELECT vezes! Cabe ao desenvolvedor decidir indexar, considerando seu conhecimento e experiência, mas dizer que PRIMARY KEYs compostos são sempre ruins é simplesmente errado.

No caso de unir tabelas, elas geralmente são as únicas PRIMARY KEY que fazem sentido! As tabelas de junção também são frequentemente a única maneira de modelar o que acontece nos negócios ou na natureza ou em praticamente todas as esferas em que consigo pensar!

Esse PK também é útil, pois covering indexpode ajudar a acelerar as pesquisas. Nesse caso, seria particularmente útil se alguém estivesse pesquisando regularmente (id do curso, id do aluno) o que, imaginamos, pode ser o caso!

Este é apenas um pequeno exemplo de onde um composto PRIMARY KEYpode ser uma boa idéia e a única maneira sensata de modelar a realidade! Do alto da minha cabeça, consigo pensar em muitos mais.

Um exemplo do meu próprio trabalho!

Considere uma tabela de voo contendo um flight_id, uma lista de aeroportos de partida e chegada e os horários relevantes e, em seguida, também uma tabela de tripulação de cabine com tripulantes!

A única maneira sensata de modelar isso é ter uma tabela de flight_crew com o flight_id e o crew_id como atributos, e o único são PRIMARY KEYé usar a chave composta dos dois campos!

Vérace
fonte
2
no exemplo do curso e dos alunos, é possível que o course_student tenha uma idchave primária e um índice exclusivo cs_student_id cs_course_ide tenha os mesmos resultados?
hackvan
2
Por que desperdiçar recursos fazendo isso? Com PK (course_id, student_id), por definição, você já possui um índice exclusivo nesses campos! Um índice exclusivo em (student_id, course_id) pode ser útil para acelerar as pesquisas - por exemplo, se você estivesse procurando estudantes que não estavam participando de nenhum curso, mas essa decisão poderia ser operacional, mas atualmente, com armazenamento relativamente barato, Eu o recomendaria, especialmente porque se pensaria que a tabela não será atualizada com muita frequência.
Vérace 24/10
11
Concordo completamente com as tabelas de links - estou trabalhando com várias no momento. No entanto, quando eu coloco meu chapéu de C #, estou trabalhando com o gerador reversepoco e construindo classes úteis (encontrar, salvar etc.) para a próxima camada. Eu encontrei um grande problema - as chaves compostas se tornam uma PITA por ter qualquer código genérico de salvar / encontrar. Sim, talvez eu possa voltar aos arquivos EDMX, mas ainda preciso contornar o código de caso especial (contar colunas Pkey?) Ou adicionar uma chave substituta artificial (não gosto e preciso de restrições adicionais de exclusividade :(). pessoas não gostam compósitos estão falando a partir do código camada App.
Richard Griffiths
Dependendo da frequência de pastilhas e da frequência de desfragmentação do índice versus janela de manutenção, esta é a melhor solução. Mas algumas opções de design são compromissos conduzidos por requisitos que podem não ser imediatamente visíveis. Mas, como um comentário disse, identifique os prós / contras dos dois cenários e faça uma escolha de design.
Jonathan Fite
O que acontece quando um aluno repete o curso? Então, a menos que os cursos separados no tempo obtenham IDs diferentes - você terá outra tabela de mapeamento. Ou adicione um campo para a data do curso que agora deve ser adicionado à chave.
Iheanyi 15/07/19
3

Minha opinião semi-educada: uma "chave primária" não precisa ser a única chave exclusiva usada para procurar dados na tabela, embora as ferramentas de gerenciamento de dados o ofereçam como seleção padrão. Portanto, para optar por ter um composto de duas colunas ou um número aleatório (provavelmente serial) gerado como a chave da tabela, você pode ter duas chaves diferentes ao mesmo tempo.

Se os valores dos dados incluírem um termo exclusivo adequado que possa representar a linha, prefiro declarar isso como "chave primária", mesmo que composta, do que usar uma chave "sintética". A chave sintética pode ter um desempenho melhor por razões técnicas, mas minha própria opção padrão é designar e usar o termo real como chave primária, a menos que você realmente precise seguir o outro caminho para fazer o serviço funcionar.

Um Microsoft SQL Server possui o recurso distinto, mas relacionado, do "índice clusterizado" que controla o armazenamento físico de dados em ordem de índice e também é usado dentro de outros índices. Por padrão, uma chave primária é criada como um índice em cluster, mas você pode escolher não em cluster, preferencialmente depois de criar o índice em cluster. Portanto, você pode ter uma coluna de identidade inteira gerada como índice clusterizado e, digamos, o nome do arquivo nvarchar (128 caracteres) como chave primária. Isso pode ser melhor porque a chave de índice em cluster é estreita, mesmo se você armazenar o nome do arquivo como o termo de chave estrangeira em outras tabelas - embora este exemplo seja um bom exemplo para não fazer isso.

Se o seu design envolve a importação de tabelas de dados que incluem uma chave primária inconveniente para identificar dados relacionados, você está praticamente preso a isso.

https://www.techopedia.com/definition/5547/primary-key descreve um exemplo de escolha entre armazenar dados com o número de segurança social de um cliente como a chave do cliente em todas as tabelas de dados ou gerar um customer_id arbitrário quando você registre-os. Na verdade, esse é um abuso grave do SSN, além de funcionar ou não; é um valor de dados pessoais e confidenciais.

Portanto, uma vantagem de usar um fato do mundo real como chave é que, sem voltar à tabela "Cliente", você pode recuperar informações sobre elas em outras tabelas - mas também é um problema de segurança de dados.

Além disso, você terá problemas se o SSN ou outra chave de dados tiver sido gravada incorretamente, portanto, você terá o valor errado em 20 tabelas restritas, em vez de apenas no "Cliente". Enquanto o customer_id sintético não tem significado externo, não pode ter um valor errado.

Robert Carnegie
fonte
11
Aprecio especialmente a observação de que, dependendo dos dados do cliente como chave, mesmo os dados exclusivos conhecidos do cliente (aqui, SSN), são quebrados se esses dados precisarem ser corrigidos.
ToolmakerSteve