É necessária uma coluna de ID exclusivo em uma tabela muitos para muitos (junção)?

22

Iniciando alguns projetos com o EF, mas eu tinha algumas perguntas sobre junção de tabelas e chaves, etc. Digamos que eu tenho uma tabela de aplicativos e uma tabela de permissões. Os aplicativos têm muitas permissões e cada permissão pode pertencer a muitos aplicativos (muitos para muitos).

Agora, as tabelas de Aplicação e Permissão são fáceis:

Applications
--------------
PK  ApplicationID
    Name

Permissions
--------------
PK  PermissionID
    Name

Mas qual é a MELHOR maneira de fazer a tabela de junção? Eu tenho essas duas opções:

ApplicationPermissions
-----------------------
PK  ApplicationPermissionID
CU  ApplicationID
CU  PermissionID

OU

ApplicationPermissions
-----------------------
CPK ApplicationID
CPK PermissionID

PK = Primary Key
CPK = Composite Primary Key
CU = Composite Unique Index

Você já foi queimado fazendo isso de uma maneira sobre a outra? é estritamente preferência? Ocorreu-me que muitas "diferenças" serão abstraídas pelo meu padrão de repositório (por exemplo, eu quase nunca criaria um objeto de permissão inteiro e o adicionaria a um aplicativo, mas por ID ou nome exclusivo ou alguma coisa), mas acho que estou procurando histórias de horror, de um jeito ou de outro.

solidau
fonte

Respostas:

20

Eu acredito que você quer dizer tabela "junção", não tabela "junção".

Não é necessário que uma tabela de junção tenha seu próprio campo de ID. Você nunca precisaria ingressar ou filtrar esse ID. Você ingressaria ou filtraria apenas os IDs das tabelas que está mapeando. Um ID em uma tabela de junção é um desperdício de espaço em disco.

Portanto, a opção "melhor" é evitar o ID. Normalmente, uma tabela de junção terá 2 índices de cobertura. Cada índice de cobertura usa um dos IDs mapeados como o campo de classificação principal.

Mas "melhor" não é de longe. É um problema muito pequeno ter um campo de ID redundante. Você não terá nenhuma história de terror com uma pequena quantidade de disco desperdiçado. O ID não "roubará" o índice em cluster porque você não deseja agrupar no combo mapeado.

Se sua estrutura deseja que todas as tabelas tenham um ID, faça isso. Se os padrões de banco de dados de sua equipe determinam que todas as tabelas devem ter um ID, então escolha. Caso contrário, evite-o.

mike30
fonte
2
Bem, você já declarou que adicionar um ID é uma concessão menor, facilmente superada pelos benefícios em potencial, então parece-me que (dado que ter um ID exclusivo em todas as tabelas é mais ou menos a melhor prática na maioria dos DBMS e ORM) você recomendaria ter um código como a opção "melhor" ou "padrão", em vez de não ter um.
Robert Harvey
4
"Você nunca precisaria ingressar ou consultar esse ID" - dizer "nunca" em uma situação de tecnologia está convidando para que isso aconteça. Dizendo isso, lá são momentos em que você se junte a que se juntam mesa (sim, eu ouvi-lo referido como um "join" mesa mais de uma tabela "junção") para uma quarta mesa, porque as entidades unidas são de fato um objeto de negócios próprio.
Jesse C. Slicer
4
@RobertHarvey. Um ID é uma boa prática para entidades. Mas uma junção é mais um detalhe de implementação para muitas relações, não uma entidade por si só. Mas, como aponta Jesse C. slider, há casos em que uma junção pode ser considerada uma entidade comercial.
mike30
1
"desperdício de espaço em disco." - Eu acho que alguns mecanismos (InnoDB?) Criam uma chave primária (interna) de qualquer maneira, se você não criar uma - você pode não ganhar espaço em disco por não ter uma.
Alex
@Alex. Você coloca uma PK composta nos IDs mapeados.
precisa saber é o seguinte
11

Ao longo dos anos, adquiri o hábito de atribuir a cada tabela "TableName" uma chave primária gerada automaticamente "TableNameID", sem exceções, nem mesmo para tabelas de junção. Posso dizer que nunca me arrependi disso, porque isso facilita muitas coisas ao criar código genérico que faz algo para "todas as tabelas" ou "algumas tabelas" ou "muitas linhas de várias tabelas diferentes".

Por exemplo, se alguém solicitar que você armazene algumas linhas de tabelas diferentes (ou referências a essas) em um arquivo ou na memória, por exemplo, para fins de registro, será muito útil quando você souber de antemão que só precisa armazenar exatamente uma nome da tabela e exatamente um ID inteiro, e você não precisa lidar com nenhum "caso especial".

Outra coisa, quando você inicia com PKs combinadas, provavelmente, algumas vezes mais tarde, encontra a necessidade de chaves estrangeiras combinadas (já que você pode chegar a um ponto em que deseja adicionar uma referência FK à sua ApplicationPermissionstabela). O próximo requisito pode ser que esse FK seja exclusivo em conjunto com outros atributos ou chaves estrangeiras - o que resultará em maior complexidade geral. Obviamente, nada que não seja possível manipular para os sistemas de banco de dados mais modernos, mas uma solução uniforme facilita muito a vida dos programadores.

E, finalmente, uma declaração como SELECT ... FROM TABLE WHERE TableNameID IN (id1,id2,...)funciona bem com uma única coluna como chave primária, mas nunca vi um dialeto SQL até agora que permita fazer isso com chaves combinadas. Se você sabe de antemão que nunca precisará de uma consulta como essa, tudo bem, mas não se surpreenda se amanhã você receber um requisito que será resolvido mais facilmente com esse tipo de SQL.

Obviamente, quando você espera que sua ApplicationPermissionstabela mantenha várias centenas de milhões de linhas, considere evitar algo como a ApplicationPermissionsID.

Doc Brown
fonte
Embora eu não tenha acabado escolhendo sua resposta. Eu gosto de aspectos disso. Obrigado por seus pensamentos (voto positivo).
Solidau
6

Embora a resposta de Mike seja boa, aqui estão as razões pelas quais eu adicionaria ou não um campo de identificação separado.

  1. Considere usar um campo de ID separado para a tabela de junção / junção se ela contiver campos diferentes do ID . Isso tende a notar que é uma entidade de primeira classe.

  2. Considere usar um campo de ID separado se as APIs ou qualquer lógica existente tenderem a usar campos únicos para recuperar / editar entidades. Isso pode ajudar outras pessoas a seguir seu código no contexto de um projeto maior.

  3. Não use se não houver benefício específico (KISS). A EF sabe como lidar com esse tipo de tabela e uma restrição exclusiva composta às vezes pode ser perdida quando outras pessoas estão tentando entender esse tipo de relacionamento. Além disso, ao normalizar, tento usar a menor chave possível que define exclusivamente a tupla . No seu segundo exemplo, você possui 2 chaves primárias candidatas separadas.

Zachary Yates
fonte
-5
table Person
   Id int identity(1,1) not null primary key
   ...other fields go here...
table Address
   Id int identity(1,1) not null primary key
   ...other fields go here...
table PersonAddress
   Id int identity(1,1) not null primary key
   PersonId int not null
   AddressId int not null

Lembre-se de criar um índice e uma chave estrangeira em ambos PersonIde AddressId.

Não importa o que os outros pensem ser "melhor" ou "você deveria", esta é a maneira mais simples e fácil de permitir que o banco de dados funcione corretamente.

16PlusYearsAsADeveloper
fonte
1
Eu acho que um problema com essa abordagem é o esquema permite duas PersonAddresslinhas com valores PersonIde idênticos AddressId.
Sam