É uma boa prática ter sempre uma chave primária de autoincremento?

191

Nos meus bancos de dados, tenho o hábito de ter uma chave primária inteira de incremento automático com o nome idde todas as tabelas que faço, para ter uma pesquisa exclusiva para qualquer linha específica.

Isso é considerado uma má ideia? Existem desvantagens em fazê-lo dessa maneira? Às vezes, tenho vários índices, como id, profile_id, subscriptionsonde idestá o identificador exclusivo, profile_idlinks para o exterior idde uma Profiletabela etc.

Ou há cenários em que você não deseja adicionar esse campo?

AJJ
fonte
61
Dê uma olhada no problema do tanque alemão para obter um exemplo em que um identificador simples de incremento automático é um problema. Claro que isso só importa se você estiver usando seus IDs em público.
Bergi 15/08/16
24
@ArukaJ O ponto é que vazam algumas informações sobre o sistema. Por exemplo, suponha que o banco de dados contenha postagens escritas pelo usuário, cada uma das quais obtendo um ID seqüencial. Digamos que você faça quatro postagens, cada uma com um ID: às 04:00 (20), 05:00 (25), 20:00 (100) e 21:00 (200). Observando os IDs, você pode ver que apenas cinco postagens foram adicionadas entre as 04:00 e as 05:00, enquanto 100 foram adicionadas entre as 20:00 e as 21:00. Se você estava tentando escolher um horário para um ataque de negação de serviço, isso poderia ser uma informação valiosa.
21609 Joshua Taylor #
29
Para todos que se queixam do "problema do tanque alemão" ... se a única coisa que impede alguém de acessar dados que não deveria é uma chave no seu URL ... você tem problemas maiores que GUID versus Auto INT.
Matthew Whited
11
@MatthewWhited Não se trata apenas de trocar parâmetros em um URL. Suponha que você use um site e crie o ativo 100 no momento te o ativo 120 no momento t + 60. Se você pode ver esses dois IDs (100 e 120) de forma não ofuscada, agora conhece o número total de ativos que existem, bem como aproximadamente a taxa na qual eles são criados. Isso é vazamento de informações. Isto não é puramente hipotético.
21716 Chris Hayes
15
"É uma boa prática sempre ..." Não
Brian_o 17/08/16

Respostas:

137

Nunca é uma má idéia ter um identificador de linha exclusivo garantido. Acho que não devo dizer nunca - mas vamos com a grande maioria das vezes que é uma boa ideia.

As desvantagens potenciais teóricas incluem um índice extra para manter e espaço de armazenamento extra usado. Isso nunca foi motivo suficiente para eu não usar um.

GrandmasterB
fonte
11
Isto é o que eu faço. A maioria das pessoas usa 'id' ou 'tablename_id' (como user_id). O argumento não é tipicamente se a coluna é necessária, mas qual o caminho para nomeá-la.
GrandmasterB
103
Pessoalmente, acho que o nome da tabela deve implicar o resto. TableName.idao contrário TableName.TableName_id, porque a que mais isso idse refere? Se eu tiver outro campo de identificação na tabela, prefixo-o com um nome de tabela se estiver se referindo a alguma outra tabela.
AJJ
10
@ArukaJ você mencionou que está usando SQLite. Na verdade, esse é um caso um pouco especial, pois sempre faz essa coluna 'sob o capô'. Portanto, você não está nem usando espaço extra, porque recebe um, quer queira ou não. Além disso, o rowid do SQLite é sempre um número inteiro de 64 bits. Se meu entendimento estiver correto, se você definir uma linha de incremento automático, será um alias para o rowid interno. Então você poderia sempre fazê-lo! Veja sqlite.org/autoinc.html
GrandmasterB
9
A única exceção que posso pensar é se você tiver um identificador exclusivo gerado de outra maneira; nesse caso, essa deve ser a chave primária e um ID de incremento automático é redundante.
precisa saber é o seguinte
4
@GrandmasterB: A versão atual do SQLite permite criar WITHOUT ROWIDtabelas (com explícito PRIMARY KEY) como uma otimização. Mas, caso contrário, uma INTEGER PRIMARY KEYcoluna é um alias para o rowid.
precisa saber é
92

Eu discordo de todas as respostas anteriores. Há muitas razões pelas quais é uma má idéia adicionar um campo de incremento automático em todas as tabelas.

Se você possui uma tabela na qual não há chaves óbvias, um campo de incremento automático parece uma boa ideia. Afinal, você não quer select * from blog where body = '[10000 character string]'. Você prefere select * from blog where id = 42. Eu diria que, na maioria desses casos, o que você realmente deseja é um identificador único; não é um identificador exclusivo sequencial. Você provavelmente deseja usar um identificador universalmente exclusivo.

Existem funções na maioria dos bancos de dados para gerar identificadores únicos aleatórios ( uuidno mysql, postgres. newidNo mssql). Isso permite gerar dados em vários bancos de dados, em máquinas diferentes, a qualquer momento, sem conexão de rede entre eles, e ainda mesclar dados com zero conflitos. Isso permite que você configure mais facilmente vários servidores e até data centers, como por exemplo, com microsserviços.

Isso também evita que os invasores adivinhem os URLs das páginas às quais eles não deveriam ter acesso. Se há um https://example.com/user/1263, provavelmente existe https://example.com/user/1262também. Isso pode permitir a automação de uma exploração de segurança na página de perfil do usuário.

Também existem muitos casos em que uma coluna de uuid é inútil ou até prejudicial. Digamos que você tenha uma rede social. Há uma usersmesa e uma friendsmesa. A tabela de amigos contém duas colunas de ID do usuário e um campo de incremento automático. Você quer 3ser amigo e 5inserir 3,5no banco de dados. O banco de dados adiciona um ID de incremento automático e armazena 1,3,5. De alguma forma, o usuário 3clica no botão "adicionar amigo" novamente. Você insere 3,5no banco de dados novamente, o banco de dados adiciona um ID de incremento automático e insere 2,3,5. Mas agora 3e 5são amigos um do outro duas vezes! Isso é um desperdício de espaço e, se você pensar sobre isso, também é a coluna de incremento automático. Tudo o que você precisa para ver se aebsão amigos é selecionar para a linha com esses dois valores. Eles são, juntos, um identificador de linha exclusivo. (Você provavelmente escreveria alguma lógica para ter certeza 3,5e 5,3deduplicação.)

Ainda existem casos em que os IDs seqüenciais podem ser úteis, como na criação de um encurtador de URL, mas principalmente (e mesmo com o encurtador de URL) um ID exclusivo gerado aleatoriamente é o que você realmente deseja usar.

TL; DR: use UUIDs em vez de incremento automático, se você ainda não tem uma maneira única de identificar cada linha.

Filip Haglund
fonte
26
O problema com os UUIDs é que eles ocupam muito espaço na maioria das tabelas. Use o identificador exclusivo certo para cada tabela.
Stephen Stephen
49
O parágrafo inteiro sobre exclusividade é discutível - a exclusividade pode ser aplicada, com ou sem uma chave primária. Além disso, os UUIDs são melhores no lado teórico, mas são difíceis de usar ao depurar / executar tarefas de DBA ou ao executar qualquer coisa que não esteja "resistindo a ataques".
11
Outro cenário em que os UUIDs são melhores: implementar uma operação PUT idempotente, para que você possa tentar novamente solicitações com segurança sem introduzir linhas duplicadas.
yurez 16/08/19
21
No ponto "adivinhação de URL", ter um ID exclusivo (seqüencial ou não) não implica expor esse ID aos usuários do aplicativo.
Dave Sherohman
7
Puramente do ponto de vista do banco de dados, esta resposta está completamente errada. O uso de UUIDs em vez de números inteiros com incremento automático aumenta os índices muito rapidamente e afeta adversamente o desempenho e o consumo de memória. Se você estiver falando do ponto de vista do serviço da web ou aplicativo da web, deve haver uma camada entre o banco de dados e o front end de qualquer maneira. Qualquer outra coisa é um design ruim. Usar dados como chave primária é ainda pior. As chaves primárias devem ser usadas apenas na camada de dados, em nenhum outro lugar.
Drunken Code Monkey
60

As teclas auto-incrementais têm principalmente vantagens.

Mas algumas desvantagens possíveis podem ser:

  • Se você possui uma chave comercial, também é necessário adicionar um índice exclusivo nessas colunas para aplicar as regras comerciais.
  • Ao transferir dados entre dois bancos de dados, especialmente quando os dados estão em mais de uma tabela (por exemplo, mestre / detalhes), isso não é direto, pois as seqüências não são sincronizadas entre os bancos de dados e você precisará criar uma tabela de equivalência primeiro usando o comando chave comercial como uma correspondência para saber qual ID do banco de dados de origem corresponde a qual ID no banco de dados de destino. Isso não deve ser um problema ao transferir dados de / para tabelas isoladas.
  • Muitas empresas têm ferramentas de relatório ad-hoc, gráficas, apontar e clicar, arrastar e soltar. Como os IDs autoincrementais não têm sentido, esse tipo de usuário achará difícil entender os dados fora do "aplicativo".
  • Se você acidentalmente modificar a chave comercial, é provável que você nunca recupere essa linha porque não tem mais algo para os humanos identificá-la. Isso causou uma falha na plataforma BitCoin uma vez .
  • Alguns designers adicionam um ID a uma tabela de junção entre duas tabelas, quando o PK deve simplesmente ser composto pelos dois IDs estrangeiros. Obviamente, se a tabela de junção estiver entre três ou mais tabelas, um ID autoincremental fará sentido, mas você precisará adicionar uma chave exclusiva quando for aplicada à combinação de FKs para impor regras de negócios.

Aqui está uma seção de artigo da Wikipedia sobre as desvantagens das chaves substitutas.

Tulains Córdova
fonte
13
Culpar a falha do mt.gox em chaves substitutas parece um tanto duvidoso. O problema era que eles incluíam todos os campos em sua chave composta, mesmo campos mutáveis ​​/ maleáveis.
CodesInChaos
6
Uma desvantagem "social" do uso de chaves de incremento automático é que, às vezes, "a empresa" assume que nunca deve haver lacunas e demandas para saber o que aconteceu com as linhas ausentes que ocorrem quando ocorre uma falha na inserção (reversão de transação).
Rick Ryker
4
Outra desvantagem é que, se o sistema crescer tanto que você precisará fragmentar o banco de dados, não poderá mais usar o incremento automático para produzir uma chave globalmente exclusiva. Quando você chega a esse ponto, pode ter muito código confiando nessa suposição. Existem outras maneiras de produzir um identificador exclusivo que continuará funcionando se o banco de dados estiver fragmentado.
kasperd
11
@Voo Não é garantido que o banco de dados escolhido suporte isso. E tentar implementá-lo em uma camada mais alta que o próprio banco de dados significa que você perde algumas das garantias que o SQL lhe daria. Finalmente, qualquer atribuição centralizada de IDs aumentará a latência se você tiver um sistema distribuído.
kasperd
11
@Voo É claro que, independentemente da escala do sistema, não se deve fazer muitas suposições sobre a natureza dos IDs autoincrementados. Se você tiver apenas um único banco de dados, eles serão atribuídos em ordem, mas não há garantia de que eles sejam confirmados em ordem. E pode haver uma lacuna na sequência porque nem todas as transações são confirmadas.
kasperd
20

Apenas para ser contrário, não, você NÃO precisa sempre ter um AutoInc PK numérico.

Se você analisar seus dados cuidadosamente, geralmente identifica chaves naturais nos dados. Geralmente, esse é o caso quando os dados têm um significado intrínseco para os negócios. Às vezes, as PKs são artefatos de sistemas antigos que os usuários corporativos utilizam como um segundo idioma para descrever os atributos de seu sistema. Vi números de VIN de veículos usados ​​como chave primária de uma tabela "Veículo" em um sistema de gerenciamento de frotas, por exemplo.

Seja como for, se você já possui um identificador exclusivo, use-o. Não crie uma segunda chave primária sem sentido; é um desperdício e pode causar erros.

Às vezes, você pode usar um AutoInc PK para gerar um valor significativo para o cliente, por exemplo, números de política. Definir o valor inicial para algo sensato e aplicar regras de negócios sobre zeros à esquerda, etc. Essa é provavelmente uma abordagem do "melhor dos dois mundos".

Quando você tiver um pequeno número de valores relativamente estáticos, use valores que façam sentido para o usuário do sistema. Por que usar 1,2,3 quando você pode usar L, C, H, onde L, H e C representam Vida, Carro e Lar em um contexto de "Tipo de Política" de seguro ou, voltando ao exemplo do VIN, que tal usar "TO "para a Toyota? Todos os carros da Toyata têm um VIN que inicia o "TO". É uma coisa a menos para os usuários lembrarem, torna menos provável a introdução de erros de programação e do usuário e pode até ser um substituto utilizável para uma descrição completa nos relatórios de gerenciamento, tornando os relatórios mais simples. para escrever e talvez mais rápido para gerar.

Um desenvolvimento adicional disso provavelmente é "uma ponte longe demais" e geralmente não a recomendo, mas estou incluindo-a por completo e você pode achar um bom uso para ela. Ou seja, use a Descrição como chave primária. Para dados que mudam rapidamente, isso é uma abominação. Para dados muito estáticos relatados em Todo o tempo , talvez não. Apenas mencionando, para que fique lá como uma possibilidade.

Eu uso AutoInc PKs, apenas envolvo meu cérebro e procuro melhores alternativas primeiro. A arte do design de banco de dados está criando algo significativo que pode ser consultado rapidamente. Ter muitas junções dificulta isso.

EDIT Outro caso crucial em que você não precisa de uma PK gerada automaticamente é o caso de tabelas que representam a interseção de duas outras tabelas. Para manter a analogia do carro, um carro tem 0..n acessórios, cada acessório pode ser encontrado em muitos carros. Portanto, para representar isso, você cria uma tabela Car_Accessory contendo as PKs de carro e acessório e outras informações relevantes sobre o link Datas etc.

O que você normalmente não precisa é de um AutoInc PK nesta tabela - ele só será acessado através do carro "diga-me quais são os acessórios deste carro" ou no acessório "diga-lhes quais carros têm este acessório"

mcottle
fonte
4
> Todos os carros da Toyata têm um VIN que inicia "TO" Isso simplesmente não é verdade. Eles começam com "JT" se fabricados no Japão. Toyotas Americano-construídos têm completamente diferente VINs en.wikibooks.org/wiki/...
Monty mais dura
17
Don't create a second, meaningless primary key; it's wasteful and may cause errors.No entanto, se a maneira como você estabelece a exclusividade de um registro é uma combinação de 6 colunas, a junção das 6 o tempo todo é muito propensa a erros. Os dados naturalmente têm uma PK, mas é melhor usar uma idcoluna e uma restrição exclusiva nessas 6 colunas.
Brad
14
Admito que algumas dessas sugestões levam um pouco longe para mim. Sim, ser pragmático é bom, mas não posso contar com que frequência alguém jurou a vida de seu primogênito que algum atributo fora do domínio permanecerá único pelo resto dos dias. Bem, geralmente isso funcionou bem até a segunda semana após o lançamento, quando as primeiras cópias apareceram. ;) Usar uma "descrição" como PK está muito longe.
AnoE
2
@ Monty, meu mal, você está certo. Memória falível, faz 20 anos que eu arquitetei os sistemas de gerenciamento de frota. Não, o VIN não era a chave principal :) Eu usei um AutoInc Asset_ID IIRC que leva a algo que eu esqueci. Tabelas que vinculam os relacionamentos muitos-para-muitos em que você vincula, digamos, carro a acessório (por exemplo, teto solar) Muitos carros têm muitos acessórios; portanto, você precisa de uma tabela "Car_Accessory" que contém Car_ID e Accessory_ID, mas absolutamente NÃO precisa Car_Accesory_ID como um PK do AutoInc.
Mcottle
7
É realmente incrível o quão poucas "chaves naturais" VERDADEIRAMENTE imutáveis ​​existem. SSN's? Não, eles podem mudar. É raro, mas pode acontecer. Nomes de usuário? Não. Eventualmente, alguém terá um motivo comercial válido para mudar. O VIN geralmente é um exemplo de livro didático, mas não existem muitos outros. Até os endereços residenciais podem mudar, dadas as mudanças de nome das ruas.
Erik Funkenbusch
12

Muitas tabelas já possuem um ID exclusivo natural. Não adicione outra coluna de ID exclusiva (incremento automático ou outro) nessas tabelas. Use o ID exclusivo natural. Se você adicionar outro ID exclusivo, terá essencialmente uma redundância (duplicação ou dependência) em seus dados. Isso vai contra os princípios da normalização. Um ID exclusivo depende do outro para precisão. Isso significa que eles devem ser mantidos perfeitamente sincronizados o tempo todo em todos os sistemas que gerenciam essas linhas. É apenas mais uma fragilidade na integridade dos dados que você realmente não deseja gerenciar e validar a longo prazo.

Atualmente, a maioria das tabelas atualmente não precisa do aumento de desempenho muito menor que uma coluna de ID exclusiva adicional daria (e às vezes isso prejudica o desempenho). Como regra geral em TI, evite redundâncias como a praga! Resista a todos os lugares que lhe forem sugeridos. É anátema. E preste atenção na citação. Tudo deve ser o mais simples possível, mas não mais simples. Não tenha dois IDs únicos onde um será suficiente, mesmo que o natural pareça menos organizado.

Brad Thomas
fonte
3
Você não deve usar apenas IDs "naturais" como chaves primárias se estiver absolutamente garantido que nunca mudará? Por exemplo, você não deve usar o número da carteira de motorista como chave primária, porque se uma pessoa obtiver uma nova carteira de motorista, será necessário atualizar não apenas essa tabela, mas todas as tabelas com chaves estrangeiras que a referenciam!
ekolis
11
Há várias razões pelas quais o número da carteira de motorista não se qualifica como um ID exclusivo natural. Primeiro, alguns deles são derivados de outros dados, como data e nome de nascimento. Eles não são garantidos exclusivos entre os estados. E, como exemplo, quando uma pessoa recebe novamente uma licença com o mesmo número, mas talvez com um prazo estendido, o que acontece então? Eles têm uma licença diferente com o mesmo número. Um ID natural ainda precisa cumprir as propriedades básicas de uma chave primária. O número da carteira de motorista (pelo menos nos EUA) apresenta algumas deficiências nesse sentido.
Brad Thomas
11
OK, acho que entendi mal a definição de identificação natural; Eu pensei que era apenas um ID definido pelas regras de negócios, independentemente de ser realmente imutável.
Ekolis
10

Em sistemas maiores, o ID é impulsionador da consistência; use-o quase em qualquer lugar. Nesse contexto, chaves primárias individuais NÃO são recomendadas, elas são caras na linha de fundo (leia o porquê).

Toda regra tem uma exceção, portanto, talvez você não precise do ID de incremento automático inteiro nas tabelas temporárias usadas para exportação / importação e em tabelas unidirecionais ou temporárias semelhantes. Você também prefere GUIDs em vez de IDs em sistemas distribuídos.

Muitas respostas aqui sugerem que a chave exclusiva existente deve ser usada. Bem, mesmo que tenha 150 caracteres? Acho que não.

Agora meu ponto principal:

Parece que os oponentes do ID inteiro de incremento automático estão falando sobre pequenos bancos de dados com até 20 tabelas. Lá eles podem pagar uma abordagem individual para cada tabela.

MAS, uma vez que você tenha um ERP com mais de 400 tabelas, ter um ID de incremento automático inteiro em qualquer lugar (exceto os casos mencionados acima) faz muito sentido. Você não confia em outros campos exclusivos, mesmo que estejam presentes e protegidos por exclusividade.

  • Você se beneficia da convenção universal de economia de tempo, economia de esforço e fácil de lembrar.
  • Na maioria dos casos, você JOINtabela, sem precisar verificar quais são as chaves.
  • Você pode ter rotinas de código universal trabalhando com sua coluna de incremento automático.
  • Você pode estender seu sistema com novas tabelas ou plug-ins de usuário não previstos anteriormente, simplesmente consultando IDs de tabelas existentes. Eles já estão lá desde o início, sem custos para adicioná-los adicionalmente.

Em sistemas maiores, pode valer a pena ignorar os benefícios menores dessas chaves primárias individuais e usar consistentemente o ID de incremento automático automático na maioria dos casos. O uso de campos exclusivos existentes como chaves primárias talvez economize alguns bytes por registro, mas o armazenamento adicional ou o tempo de indexação não causam problemas nos mecanismos de banco de dados atuais. Na verdade, você está perdendo muito mais dinheiro e recursos com o tempo perdido dos desenvolvedores / mantenedores. O software de hoje deve ser otimizado para o tempo e o esforço dos programadores - que abordagem com IDs consistentes é muito melhor.

miroxlav
fonte
Por experiência pessoal, concordo plenamente com a segunda metade da sua resposta. Você precisará de chaves únicas globalmente com muito menos frequência do que os índices rápidos e compactos. Se você precisar de uma, crie uma tabela GlobalEntities com um ID gerado automaticamente e uma coluna UUID. Em seguida, adicione uma chave estrangeira ExGlobalEntityId à tabela Customers, por exemplo. Ou use um hash de alguns dos valores.
Drunken Code Monkey
8

Não é uma boa prática para projetos supérfluos. Ou seja, não é uma boa prática sempre ter um incremento automático na chave primária quando não for necessário.

Vamos ver um exemplo em que não é necessário.

Você tem uma tabela para artigos - esta possui uma chave primária int ide uma coluna varchar denominada title.

Você também tem uma tabela cheia de categorias de artigos - idchave primária int, varchar name.

Uma linha da tabela Artigos tem um idde 5 e um title "Como cozinhar ganso com manteiga". Você deseja vincular esse artigo às seguintes linhas da tabela Categorias: "Fowl" ( identificação : 20), "Ganso" ( identificação : 12), "Culinária" ( identificação : 2), "Manteiga" (identificação: 9) .

Agora, você tem 2 tabelas: artigos e categorias. Como você cria o relacionamento entre os dois?

Você pode ter uma tabela com 3 colunas: id (chave primária), article_id (chave estrangeira), category_id (chave estrangeira). Mas agora você tem algo como:

| id | a_id c_id |
| 1 | 5 20
| 2 5 12
| 3 5 2

Uma solução melhor é ter uma chave primária composta de 2 colunas.

| a_id c_id |
| 5 20
| 5 12
| 5 2

Isso pode ser feito fazendo:

create table articles_categories (
  article_id bigint,
  category_id bigint,
  primary key (article_id, category_id)
) engine=InnoDB;

Outro motivo para não usar um número inteiro de incremento automático é se você estiver usando UUIDs para sua chave primária.

Os UUIDs são, por definição, únicos, o que realiza o mesmo que o uso de números inteiros únicos. Eles também têm seus próprios benefícios adicionais (e contras) sobre números inteiros. Por exemplo, com um UUID, você sabe que a string exclusiva à qual você está se referindo aponta para um registro de dados específico; isso é útil nos casos em que você não possui um banco de dados central ou onde os aplicativos têm a capacidade de criar registros de dados offline (depois carregue-os no banco de dados posteriormente).

No final, você não precisa pensar nas chaves primárias como algo. Você precisa pensar neles como a função que desempenham. Por que você precisa de chaves primárias? Ser capaz de identificar exclusivamente conjuntos específicos de dados de uma tabela usando um campo que não será alterado no futuro. Você precisa de uma coluna específica chamada idpara fazer isso ou pode basear essa identificação exclusiva em outros dados (imutáveis)?

anw
fonte
7

Ou há cenários em que você não deseja adicionar esse campo?

Certo.

Primeiro de tudo, existem bancos de dados que não possuem incrementos automáticos (por exemplo, Oracle, que certamente não é um dos menores concorrentes). Essa deve ser a primeira indicação de que nem todo mundo gosta ou precisa deles.

Mais importante, pense sobre o que realmente é o ID - é a chave principal para seus dados. Se você possui uma tabela com uma chave primária diferente, não precisa de um ID e não deve ter um. Por exemplo, uma tabela (EMPLOYEE_ID, TEAM_ID)(onde cada funcionário pode estar em várias equipes simultaneamente) possui uma chave primária claramente definida, consistindo nesses dois IDs. Adicionar uma IDcoluna de incremento automático , que também é uma chave primária para esta tabela, não faria sentido. Agora você está carregando duas chaves primárias e a primeira palavra em "chave primária" deve dar uma dica de que você realmente deve ter apenas uma.

AnoE
fonte
9
(Não é um usuário da Oracle que perdoa a pergunta, mas) a Oracle não usa o Sequence da mesma maneira que outros usam o incremento automático / identidade? Está dizendo que o Oracle não tem um tipo de dados de incremento automático, é apenas um argumento somático?
Brad
Bem, isso foi apenas um pequeno ponto; a parte principal é que um ID em execução não é apropriado para todas as tabelas, portanto, se acostumar a colocar um ID automático em cada tabela pode não ser o mais sábio.
AnoE
não existem duas chaves primárias, há apenas uma chave primária e todos os demais são chamados de chaves candidatas, se eles podem servir como chaves primárias também ..
Rahul Tyagi
7

Normalmente, uso uma coluna "identidade" (número inteiro com aumento automático) ao definir novas tabelas para dados "de longa duração" (registros que espero inserir uma vez e permanecer indefinidamente, mesmo que eles acabem "excluídos logicamente" definindo um campo de bit )

Existem algumas situações em que não consigo usá-las, a maioria delas se resume a cenários em que uma tabela em uma instância do banco de dados não pode ser a fonte autorizada para novos valores de ID:

  • Quando IDs incrementais seriam informações demais para um invasor em potencial. O uso de uma coluna de identidade para serviços de dados "voltados para o público" torna você vulnerável ao "Problema do tanque alemão"; se o ID do registro 10234 existir, é lógico que o registro 10233, 10232, etc. existe, retorne ao menos para o registro 10001 e, em seguida, é fácil verificar se o registro 1001, 101 e 1 é para descobrir onde a coluna de identidade foi iniciada. Os GUIDs da V4 compostos principalmente por dados aleatórios interrompem esse comportamento incremental por design, de modo que, apenas porque um GUID existe, um GUID criado por incremento ou decremento de um byte do GUID não existe necessariamente, dificultando o invasor usar um serviço destinado para recuperação de registro único como uma ferramenta de despejo. Existem outras medidas de segurança que podem restringir melhor o acesso, mas isso ajuda.
  • Nas tabelas de referência cruzada M: M. Essa é meio que uma dádiva, mas eu já vi isso antes. Se você tiver um relacionamento muitos para muitos entre duas tabelas em seu banco de dados, a solução inicial será uma tabela de referência cruzada contendo colunas de chave estrangeira que referenciam a PK de cada tabela. A PK desta tabela deve ser praticamente sempre uma chave composta das duas chaves estrangeiras, para obter o comportamento interno do índice e garantir a exclusividade das referências.
  • Quando você planeja inserir e excluir em massa essa tabela muito. Provavelmente, a maior desvantagem das colunas de identidade é a confusão extra que você deve enfrentar ao inserir linhas de outra tabela ou consulta, na qual deseja manter os valores-chave da tabela original. Você precisa ativar a "inserção de identidade" (no entanto, isso é feito no seu DBMS) e, em seguida, verifique manualmente se as chaves que você está inserindo são exclusivas e, quando terminar a importação, defina o contador de identidade no os metadados da tabela para o valor máximo presente. Se essa operação acontecer muito nesta tabela, considere um esquema PK diferente.
  • Para tabelas distribuídas.As colunas de identidade funcionam muito bem para bancos de dados de instância única, pares de failover e outros cenários em que uma instância de banco de dados é a única autoridade em todo o esquema de dados a qualquer momento. No entanto, é tão grande que você pode ir e ainda ter um computador com rapidez suficiente. A replicação ou o envio do log de transações podem obter cópias adicionais somente leitura, mas também há um limite para a escala dessa solução. Mais cedo ou mais tarde, você precisará de duas ou mais instâncias do servidor, manipulando inserções de dados e depois sincronizando-se. Quando essa situação ocorrer, você desejará um campo GUID em vez de um campo incremental, porque a maioria dos DBMSes vem pré-configurados para usar uma parte dos GUIDs que eles geram como um identificador específico da instância e, em seguida, gera o restante do identificador aleatoriamente ou incrementalmente. Em ambos os casos,
  • Quando você precisa aplicar exclusividade em várias tabelas no banco de dados.É comum em sistemas contábeis, por exemplo, gerenciar o Razão (com uma linha para cada crédito ou débito de todas as contas que já ocorreram, para que fique muito grande muito rapidamente) como uma sequência de tabelas, cada uma representando um mês civil / ano. As visualizações podem ser criadas para conectá-las para geração de relatórios. Logicamente, essa é uma tabela muito grande, mas dividi-la facilita os trabalhos de manutenção do banco de dados. No entanto, apresenta o problema de como gerenciar inserções em várias tabelas (permitindo que você comece a registrar transações no próximo mês enquanto ainda fecha o último) sem terminar com chaves duplicadas. Novamente, os GUIDs, em vez de colunas inteiras de identidade, são a solução ideal, pois o DBMS foi projetado para gerá-los de uma maneira verdadeiramente única,

Existem soluções alternativas que permitem o uso de colunas de identidade nessas situações, como eu já mencionei, mas na maioria delas, a atualização da coluna inteira de identidade para um GUID é mais simples e resolve o problema mais completamente.

KeithS
fonte
11
Há casos em que você ainda pode precisar de ID nas tabelas M: N (usando colunas ID, ID_M, ID_N) devido à anexação de propriedades às instâncias da sua relação M: N.
precisa saber é
Os GUIDS V4 não garantem o uso de um PNRG criptograficamente forte, portanto você não deve confiar nele para o seu primeiro exemplo imo (embora se o seu mecanismo db fizer promessas mais fortes, você pode estar bem, mas isso não é portátil). Caso contrário, um post bem fundamentado.
Voo
11
@miroxlav - eu diria que, se uma tabela tiver metadados adicionais suficientes em relação ao relacionamento que uma PK separada fora das duas FKs é uma boa idéia, não será mais uma tabela de referência cruzada; é sua própria entidade que faz referência aos dois outros.
Keiths
@Voo - Você está certo, não há garantia de que os GUIDs V4 sejam criptograficamente aleatórios, apenas únicos (como todos os GUIDs). No entanto, o número de caças dos caças a jato nos EUA também não é gerado a partir de dados / algoritmos de sementes criptograficamente aleatórios. O que você realmente está procurando é um domínio pouco povoado; um GUID V4 possui 112 bytes de dados aleatórios, capazes de identificar exclusivamente registros 5e33.
Keiths
Para colocar esse número em perspectiva, todo homem, mulher e criança no planeta (todos os 7 bilhões) poderia ter 741 trilhões de pontos de dados catalogados e identificados individualmente em nosso banco de dados, e ainda estaríamos usando apenas um valor GUID por bilhão disponível. O Big Data, como uma indústria global, não está nem perto dessa escala de conhecimento. Mesmo considerando um padrão para a geração de GUID, existem outras fontes de entropia envolvidas, como a ordem na qual os dados entram no sistema e recebem um GUID atribuído.
Keiths
7

Uma chave primária de incremento automático (identidade) é uma boa idéia, exceto para observar que não faz sentido fora do contexto do banco de dados e de clientes imediatos desse banco de dados. Por exemplo, se você transferir e armazenar alguns dados em outro banco de dados, e depois gravar dados diferentes nas duas tabelas, os IDs divergirão - ou seja, dados com um ID de 42 em um banco de dados não corresponderão necessariamente aos dados com um id de 42 no outro.

Dado isso, se ainda for necessário identificar linhas exclusivamente fora do banco de dados (e é frequentemente), você deverá ter uma chave diferente para esse fim. Uma chave de negócios cuidadosamente selecionada serve, mas muitas vezes você acaba na posição de um grande número de colunas necessárias para garantir a exclusividade. Outra técnica é ter uma coluna de ID como uma chave primária em cluster de incremento automático e outra coluna de identificador único (guid) como uma chave exclusiva não em cluster, com o objetivo de identificar exclusivamente a linha onde quer que ela exista no mundo. O motivo de você ainda ter uma chave de incremento automático nesse caso é porque é mais eficiente agrupar e indexar a chave de incremento automático do que fazer o mesmo em um guia.

Um caso em que você pode não querer uma chave de incremento automático seria uma tabela muitos para muitos, onde a chave primária é um composto das colunas de ID de duas outras tabelas (você ainda pode ter uma chave de incremento automático aqui, mas eu não entendo o motivo disso).

Outra pergunta é o tipo de dados da chave incrementada automaticamente. O uso de um Int32 fornece um intervalo grande, mas relativamente limitado de valores. Pessoalmente, uso frequentemente colunas bigint para o ID, para praticamente nunca precisar me preocupar com a falta de valores.

MatthewToday
fonte
6

Como outras pessoas defenderam uma chave primária de incremento, farei uma para uma GUID:

  • É garantido que seja único
  • Você pode ter menos uma viagem ao banco de dados para obter dados em seu aplicativo. (Para uma tabela de tipos, por exemplo, você pode armazenar o GUID no aplicativo e usá-lo para recuperar o registro. Se você usa uma identidade, precisa consultar o banco de dados por nome e já vi muitos aplicativos que fazem isso para obter a PK e depois consulta novamente para obter todos os detalhes).
  • É útil para ocultar dados. www.domain.com/Article/2 Permite que você tenha apenas dois artigos, enquanto www.domain.com/article/b08a91c5-67fc-449f-8a50-ffdf2403444a não me diz nada.
  • Você pode mesclar registros de diferentes bancos de dados facilmente.
  • MSFT usa GUIDS para identidade.

Editar: ponto duplicado

Lógica de três valores
fonte
5
-1. Não é garantido que um GUID / UUID seja exclusivo e não seja 100% exclusivo. Um GUID ainda é de tamanho finito; portanto, em algum momento, você pode arriscar obter uma duplicata, embora seja altamente improvável. Seu ponto de vista sobre menos viagens ao banco de dados também é inválido - por que você não pode armazenar o ID principal no aplicativo, como pode com a chave GUID?
Niklas H
2
Jeff Atwood diz que é muito melhor do que eu jamais pude. blog.codinghorror.com/primary-keys-ids-versus-guids
Three Value Logic
Por que você não pode armazenar o ID principal no seu aplicativo? Porque o banco de dados cria. Se você executar suas sementes em um banco de dados vazio, poderá assumir que o ID será 1. E se você executar o mesmo script em um banco de dados com dados? O ID não será 1.
Three Value Logic
Você não disse nada sobre a criação de IDs no aplicativo - você acabou de escrever "armazenar". Mas se for necessário criar o ID fora do banco de dados, sim, um GUID pode ser a resposta.
Niklas H
2
Eu acrescentaria que eles escalam melhor. Os bancos de dados NoSQL de big data, como o Cassandra, nem mesmo suportam chaves de auto incremento.
Karl Bielefeldt
2

Como princípio do bom design, todas as tabelas devem ter uma maneira confiável de identificar exclusivamente uma linha. Embora seja para isso que serve uma chave primária, ela nem sempre exige a existência de uma chave primária. Adicionar uma chave primária a todas as tabelas não é uma prática ruim, pois fornece identificação de linha exclusiva, mas pode ser desnecessário.

Para manter relacionamentos confiáveis ​​entre as linhas de duas ou mais tabelas, é necessário fazê-lo através de chaves estrangeiras, daí a necessidade de chaves primárias em pelo menos algumas tabelas. A adição de uma chave primária a todas as tabelas facilita a extensão do design do banco de dados quando chega a hora de adicionar novas tabelas ou relacionamentos aos dados existentes. Planejar com antecedência é sempre uma coisa boa.

Como princípio básico (regra difícil, talvez), o valor de uma chave primária nunca deve mudar ao longo da vida útil de sua linha. É aconselhável supor que todos os dados corporativos consecutivos estão sujeitos a alterações ao longo da vida útil; portanto, quaisquer dados corporativos serão um candidato ruim para uma chave primária. É por isso que algo abstrato como um número inteiro auto-incrementado geralmente é uma boa idéia. No entanto, números inteiros auto-incrementados têm suas limitações.

Se seus dados tiverem apenas uma vida útil no seu banco de dados, números inteiros auto-incrementados são bons. Mas, como já foi mencionado em outras respostas, se você quiser que seus dados sejam compartilhados, sincronizados ou tenham uma vida fora do banco de dados, números inteiros incrementados automaticamente produzem chaves primárias ruins. Uma melhor escolha será um guia (também conhecido como "id universalmente exclusivo" do uuid).

Zenilogix
fonte
2

A pergunta e muitas das respostas perdem o ponto importante de que todas as chaves naturais de cada tabela residem apenas no esquema lógico do banco de dados e todas as chaves substitutas de cada tabela residem apenas no esquema físico do banco de dados. outras respostas discutem apenas os benefícios relativos de chaves substitutas de número inteiro versus GUID, sem discutir os motivos pelos quais as chaves substitutas são usadas corretamente e quando.

BTW: evitemos o uso da chave primária do termo mal definido e impreciso . É um artefato de modelos de dados pré-relacionais que primeiro foi cooptado (imprudentemente) no modelo relacional e depois cooptado de volta ao domínio físico por vários fornecedores de RDBMS. Seu uso serve apenas para confundir a semântica.

Observe no modelo relacional que, para que o esquema lógico do banco de dados esteja na primeira forma normal , toda tabela deve ter um conjunto de campos visível ao usuário, conhecido como chave natural, que identifique exclusivamente cada linha da tabela. Na maioria dos casos, essa chave natural é facilmente identificada, mas, ocasionalmente, é preciso construir, seja como campo de desempate ou de outra forma. No entanto, essa chave construída ainda é sempre visível ao usuário e, portanto, sempre reside no esquema lógico do banco de dados.

Por outro lado, qualquer chave substituta em uma tabela reside puramente no esquema físico do banco de dados (e, portanto, sempre deve, por razões de segurança e manutenção da integridade do banco de dados, ser totalmente invisível para os usuários do banco de dados). O único motivo para a introdução de uma chave substituta é tratar de problemas de desempenho na manutenção física e no uso do banco de dados; sejam junções, replicação, várias fontes de hardware para dados ou outras.

Como o único motivo para a introdução de uma chave substituta é o desempenho, suponhamos que desejamos que ela tenha desempenho. Se o problema de desempenho em questão for unido, queremos necessariamente tornar nossa chave substituta o mais estreita possível (sem atrapalhar o hardware, portanto, números e bytes curtos geralmente ficam fora). O desempenho da junção depende da altura mínima do índice; portanto, um número inteiro de 4 bytes é uma solução natural. Se o seu problema de desempenho for a taxa de inserção, um número inteiro de 4 bytes também poderá ser uma solução natural (dependendo dos componentes internos do RDBMS). Se o problema de desempenho de uma tabela for replicação ou várias fontes de dados, além de alguma outra tecnologia de chave substituta , seja um GUID ou uma chave de duas partes (ID do host + número inteiro) pode ser mais adequado. Pessoalmente, não sou o favorito dos GUIDs, mas eles são convenientes.

Para resumir, nem todas as tabelas exigirão uma chave substituta (de qualquer tipo); elas devem ser usadas somente quando consideradas necessárias para o desempenho da tabela em consideração. Independentemente de qual tecnologia- chave substituta comum você preferir, pense cuidadosamente nas necessidades reais da tabela antes de fazer uma escolha; alterar a opção de tecnologia de chave substituta para uma tabela será um trabalho exaustivo. Documente a métrica de desempenho principal da sua tabela para que seus sucessores entendam as escolhas feitas.

Casos especiais

  1. Se seus requisitos de negócios exigirem uma numeração seqüencial de transações para fins de auditoria (ou outros) além desse campo, não será uma chave substituta; é uma chave natural (com requisitos extras). Na documentação, um número inteiro com auto incremento gera apenas chaves substitutas ; portanto, encontre outro mecanismo para gerá-lo. Obviamente, será necessário algum tipo de monitor e, se você estiver fornecendo suas transações de vários sites, um site será especial , por ser o site host designado para o monitor.

  2. Se sua tabela nunca terá mais do que cem linhas, a altura do índice será irrelevante; todo acesso será feito por uma varredura de tabela. No entanto, as comparações de seqüências de caracteres em seqüências longas ainda serão muito mais caras que a comparação de um número inteiro de 4 bytes e mais caras que a comparação de um GUID.

  3. Uma tabela de valores de código digitados por um campo de código char (4) deve ter o mesmo desempenho que um com um número inteiro de 4 bytes. Embora não tenha prova disso, uso a suposição com frequência e nunca tive motivos para lamentá-la.

Pieter Geerkens
fonte
-1

Não só não é uma boa prática, como também é descrito como um antipadrão no livro SQL Antipatterns de Bill Karwin.

Nem toda tabela precisa de uma pseudo-chave - uma chave primária com um valor arbitrário, não algo que tenha valor semântico para o modelo - e não há razão para sempre chamá-lo id.

Pedro Werneck
fonte
este não parece oferecer nada substancial sobre os pontos feitos e explicado em antes 9 respostas
mosquito
2
e por que isso pode ser importante?
Gnat
3
@gnat Porque é um livro sobre práticas recomendadas, que aborda diretamente a questão. Não é óbvio?
Pedro Werneck
3
nem um pouco. Pesquisa no Google por "reservar sql melhores práticas" mostra cerca de 900K links para mim, porque é que este ser particularmente digno
mosquito
11
@gnat Eu não vou discutir o dia todo. Você não gosta da resposta, é para isso que servem os votos negativos.
Pedro Werneck
-2

Isso é bastante universal - caso contrário, você precisaria validar que a chave é realmente única. Isso seria feito observando todas as outras chaves ... que consumiriam tempo. Ter uma chave incremental fica caro quando seu número de registro se aproxima do valor de estouro de chave.

Eu costumo tornar os ponteiros nomes de campo mais óbvios, como ref_{table}idéias semelhantes.

Se não for necessário apontar externamente para um registro, você não precisará de um ID.

Johnny V
fonte
Valor de substituição de chave?
AJJ 15/08/16
Um número inteiro não assinado tem um valor máximo de 4294967295 antes de adicionar 1 o rolará para 0. Lembre-se de adicionar um registro e excluí-lo, o contador ainda aumentará. Certifique-se de usar unsigned into tipo de campo, caso contrário, o limite é metade desse número.
Johnny V
Estouro de número inteiro - pt.wikipedia.org/wiki/Integer_overflow
Johnny V
2
Se você adicionar / remover muitas linhas, o contador de incremento automático eventualmente excederá.
Johnny V
11
Como as pessoas lidam com capotagem? E se houver registros com um ID baixo que nunca sejam excluídos, mas você estiver começando perto do fim, onde alguns IDs estão na extremidade superior de 4294967295? Pode ser feita uma "re-indexação"?
AJJ
-2

Eu não diria que isso sempre deve ser feito. Eu tenho uma tabela aqui sem chave única - e ela não precisa de uma. É um log de auditoria. Nunca haverá uma atualização; as consultas retornarão todas as alterações ao que está sendo registrado, mas é o melhor que pode ser feito razoavelmente; é necessário que um ser humano defina uma alteração incorreta. (Se o código pudesse ter desaprovado em primeiro lugar!)

Loren Pechtel
fonte
-3

Um contador de incremento automático para uma chave primária não é uma boa ideia. Isso ocorre porque você precisa voltar ao banco de dados para encontrar a próxima chave e aumentar em uma antes de inserir seus dados.

Dito isto, eu usaria geralmente o que o banco de dados pode fornecer para a chave primária, em vez de tê-la como parte do aplicativo.

Ao permitir que o banco de dados o forneça nativamente, você pode garantir que a chave seja única para o que precisa.

Obviamente, nem todos os bancos de dados o suportam. Nesse caso, eu geralmente uso uma tabela que armazena os principais depósitos e os intervalos altos e baixos gerenciados no aplicativo. Essa é a solução de melhor desempenho que encontro porque você obtém um intervalo de 10000 números e os incrementa automaticamente na instância do aplicativo. Outra instância do aplicativo pode pegar outro intervalo de números para trabalhar. Você precisa de uma primitiva de chave primária suficientemente grande, como um comprimento de 64 bits.

UUIDs que não uso como chaves primárias, porque o custo de construí-las e armazená-las é muito mais alto do que incrementar um valor longo por um. Os UUIDs ainda lidam com o paradoxo do aniversário, na medida em que uma duplicata pode teoricamente surgir.

Archimedes Trajano
fonte
3
Não. Chaves de incremento automático significa que o incremento da chave é feito automaticamente pelo banco de dados. Às vezes (estou olhando para você, Oracle!) Você precisa de uma combinação de sequência + gatilho para fazer isso, mas nunca precisa procurar o valor inserido anteriormente para a chave, adicionar 1 e usá-lo.
SQB
Com algumas estruturas de persistência, como JPA, se você deseja retornar o valor da chave que foi criada de volta ao chamador, é necessário carregar o registro para ver a chave.
Archimedes Trajano