Criando uma chave primária secundária em um banco de dados para algumas tabelas

22

Para algumas das minhas tabelas, quero adicionar "second_primary_key", que será uuid ou alguma chave longa aleatória. Eu preciso disso porque, para algumas tabelas, não quero expor números inteiros ao meu aplicativo da web. Ou seja, em uma página "/ invoices", tenho uma lista de faturas e um link para "/ invoices /: id", em que: id é um número inteiro. Não quero que um usuário saiba quantas faturas no meu sistema existem. Portanto, em vez de "/ invoices / 123", quero usar o "second_primary_key" para que o URL seja "/ invoices / N_8Zk241vNa"

O mesmo vale para outras tabelas em que quero ocultar o ID real.

Eu me pergunto, isso é uma prática comum? Qual é a melhor maneira de implementar isso?

E como é chamada essa técnica, afinal, para que eu faça uma pesquisa nela?

Dari
fonte
20
Por que não se livrar do número inteiro?
Larsbe 22/08/19
4
Você pode definir quantas chaves / índices exclusivos desejar em uma tabela.
gillifirca abuzittin
2
Talvez você deva chamá-lo de chave secundária de candidato. "Primário" sugere apenas um.
22717 Walter Mitty
4
"Segundo primário" é um oxímoro. Você tem uma chave primária e pode ter chaves secundárias.
Pare de prejudicar Monica
7
@RobbieDee existem razões válidas para não ter um banco de dados totalmente normalizado. E ter um candidato ou chave secundária não está duplicando exatamente os dados.
Machado

Respostas:

0

Você pode adicionar uma coluna UUID, mas realmente não precisa (e não deveria). Essa é uma preocupação da camada de apresentação. Você não sonharia em dizer, armazenar um valor em moeda de US $ 1.999 e 1999.

Você quer apenas uma maneira de obscurecer o valor rapidamente para o aplicativo. Você pode fazer isso no próprio aplicativo ou como uma exibição do banco de dados.

Como estamos falando apenas de um único valor, talvez seja uma criptografia bidirecional, como AES ou similar - quanto mais leve, melhor.

O hash pode ser outra possibilidade - depende se você deseja recuperar o número da fatura, pois o hash é uma maneira.

Robbie Dee
fonte
48

Ter uma "chave primária alternativa" é um conceito bem conhecido na modelagem de banco de dados relacional, é chamado de "chave alternativa" ou, às vezes, também "chave secundária". O conjunto de "chaves primárias em potencial" é chamado "chaves candidatas". Consulte https://beginnersbook.com/2015/04/alternate-key-in-dbms/

A maneira como você implementa isso é totalmente sua, especialmente se você deseja ocultar o número total de registros. Não existe a "melhor maneira", você deve verificar seus requisitos, como conjunto de caracteres permitido ou útil, tamanho máximo, se desejar que os IDs diferenciam maiúsculas de minúsculas ou não, se deseja que sejam legíveis em uma fatura impressa, se alguém deve poder revendê-los ao telefone sem erros e assim por diante.

Doc Brown
fonte
11
Também vi os termos Chave natural vs. Chave substituta usados ​​para descrever esse cenário.
Dank
2
@ Dari: você perguntou "como se chama essa técnica" - em negrito. E se a descriptografia do AES - talvez em tempo real - produz chaves do tipo que você está procurando, use-o, isso não contradiz minha resposta.
Doc Brown
1
@Dari porque acrescenta uma sobrecarga completamente desnecessário para a sua aplicação
Lamak
1
@RobbieDee Já entendemos que você não gosta de chaves alternativas, mas isso não significa que elas sejam inúteis. Eu gosto da abordagem guid porque simplifica muitos problemas.
T. Sar - Restabelece Monica
1
@RobbieDee Não usamos o SQL Server. Nós usamos o MySql. E isso acontece porque alguém criará algo no Prod, digamos com o ID 1234. No Dev, naturalmente, criamos muito mais entidades do que no prod. 1234 foi tomada há muito tempo por alguma entidade descartável para testes. Quando temos que testar uma entidade do prod, precisamos migrá-la de volta para o Dev - e sua chave primária já está em uso. A migração é muito mais fácil se as referências a essa entidade forem baseadas em guia. Mas o hibernate funciona muito melhor com uma chave primária sendo int ou long, por isso mantemos isso. Meus desenvolvedores não são preguiçosos ou ignorantes - são experientes.
precisa saber é o seguinte
9

A maioria das faturas possui um número de fatura que, pela maioria das regras contábeis, precisa ser seqüencial ou um contador pode não assinar os resultados do ano ou o IRS (ou similar em seu país) pode desejar fazer uma auditoria completa em suas guias.

Um usuário pode deduzir do número da fatura quantos clientes você atendeu ou quanto tempo levou para você alterar a estratégia de numeração nas faturas.

Quantas faturas são armazenadas no banco de dados não é uma medida do total geral de suas faturas. Existem outros meios de descobrir isso, incluindo a solicitação de relatórios do ano da Câmara de Comércio.

No entanto, bloquearia a fatura atrás de uma tela de login do usuário, para que nem todos possam solicitá-la. Em seguida, no login do usuário, eles podem usar uma metodologia ajax para solicitar suas faturas pendentes, etc. Isso protege seus dados, oculta o URL pelo ajax (normalmente ninguém pode se incomodar em ver os detalhes de como a solicitação ajax é criada) e você controla como os dados são exibidos e oferecidos.

Tschallacka
fonte
7
Uma estratégia comum usada no setor bancário (com números de cheque) é não iniciar a contagem incremental em 1, mas um número maior por esse motivo exato.
Dank
Eu acho que é por isso que o ID deve ser uma chave primária adicional, não uma substituição da antiga chave primária.
Alexander
1
Eu não chamaria de chave primária. Eu usaria uma lesma, um UUID como nome, mas, em essência, é apenas outro campo indexado na tabela. ID da cotação, número da fatura, qualquer que seja. É um campo, mas não uma chave primária. Uma chave primária precisa ser única e pode ser usada internamente para o mapeamento relacional. Se o campo estiver indexado, ele poderá ser pesquisado rapidamente por uma consulta where. userXveryY.where ('número da fatura', 'foobarbaz10'). get ();
Tschallacka 22/08
1
Você está respondendo a uma pergunta técnica com um argumento de que não é necessário devido às peculiaridades dos EUA (números de fatura sequenciais exigidos, relatórios na Câmara de Comércio). IMO isso não responde bem à pergunta.
RemcoGerlich 23/08
7

Você pode usar hashids para isso, ele foi projetado para resolver exatamente esse cenário.

Ele codificará o ID do seu banco de dados em um hash curto (semelhante ao URL de um vídeo do YouTube) e não exigirá a adição de chaves secundárias à sua tabela.

mitchdav
fonte
2
O nome é um tanto enganador, pois não é hash, mas função reversível. Mas parece ser a solução perfeita para o problema.
Louco Iogurte
2
@CrazyYoghurt verdadeiro ... eles abordaram a razão para nomeá-lo como fizeram aqui: hashids.org/#why-hashids
Eric Rei
3

Você pode criar outra chave exclusiva, mas não deve. Não pelo motivo exposto. Existem maneiras mais simples de esconder os tamanhos das mesas.

O armazenamento N_8Zk241vNacusta 12 bytes por linha na tabela e ainda mais no índice. Isso é um grande desperdício para o que você precisa.

Criptografar o número inteiro não idcusta espaço e quase nada no tempo de execução. Como você faz isso depende da sua linguagem de programação e / ou do seu banco de dados.

Observe que, com o AES, você obtém um número inteiro de 128 bits, o que significa 22 caracteres em base64, provavelmente mais do que você deseja. Uma cifra com um tamanho de bloco de 64 como DES ou 3DES fornece 11 caracteres, exatamente como você deseja.

Use chaves diferentes para tabelas diferentes.

Se tudo o que você precisa é ocultar o tamanho das tabelas, você pode usar uma sequência comum para todas as tabelas. Observe que pode haver gargalo se houver inserções frequentes em muitas de suas tabelas. Com algo como o Hibernate e um algoritmo Hi-Lo, esse problema desaparece.

maaartinus
fonte
Exatamente - armazenar esse valor apenas para ocultar outro é errado.
Robbie Dee
Isso pode funcionar nesse cenário, pois um ID de fatura não é realmente confidencial, mas como regra geral, o uso de IDs confidenciais como estrutura relacional em um banco de dados causará uma dor de cabeça real se você precisar mascarar dados em algum momento no futuro. Melhor tratá-los como um atributo.
Dank
como posso aplicar ases aqui?
Dari
@Dari Como você pode aplicar o AES a alguma coisa ? Sem conhecer seu idioma, ninguém sabe. Normalmente, o AES trabalha com a byte[], você pode escrever o seu idem quatro ou oito bytes, adicionar um número de tabela exclusivo e criptografar (a entrada deve ser exatamente de 16 bytes). Se houver modos para escolher, o BCE está certo.
maaartinus
@DanK What? Você está afirmando que a AES é insegura? Sem saber a chave, não há nada que o invasor possa fazer melhor do que para um atributo armazenado. Nada. +++ Acho que não estou entendendo seu comentário.
maaartinus
0

IMHO criando duas chaves primárias diferentes não é possível. É claro que você pode colocar esse uuid em um banco de dados para tê-lo como "alias" da chave primária atual. Você pode colocar um índice acima dessa coluna com restrição exclusiva, mas a chave primária é (a partir de sua essência) única em uma única tabela. Pode haver chave primária composta, mas não é isso que você está procurando.

Então, sugiro colocá-lo lá, mas tê-lo apenas com índice. Você pode criar um componente de manipulação para consultar dados por PK, além de outra coluna exclusiva. Ao manipular a solicitação de "/ invoices / ...", basta verificar o parâmetro - se for inteiro, procure o ID, caso contrário, procure o uuid. Ou você pode ter a pesquisa por uuid como um substituto quando a pesquisa por ID não encontrou nada.

E sobre a geração de alguns uuids "aleatórios": por que não algo como "use ID, adicione CONSTANT, converta para hexadecimal". Iniquidade de ID fornecerá exclusividade de uuid, o número hexadecimal é mais difícil de ler para mortais normais + a adição constante evitará ter uuid como 00000001.

Jarda
fonte
1
"Por que não algo como" pegue o ID, adicione CONSTANT, converta em hexadecimal "- porque isso é muito fácil de descobrir - me dê um URL e eu darei uma olhada em todas as outras faturas do sistema. IMO não há problema que isso realmente resolve, apenas os que potencialmente cria. #
227
" Ao manipular a solicitação de" / invoices / ... ", basta verificar o parâmetro - se for inteiro, procure o ID , caso contrário, procure o uuid " O objetivo (como eu entendi a pergunta) é impedir que alguém procure por ID ( /invoices/123, /invoices/124, ...) para pesquisar apenas por UUID a partir do URL.
TripeHound 22/08
Além disso, nem todos os números hexadecimais contêm letras. Seria impossível sempre distinguir entre os números inteiros subjacentes e os números hexadecimais gerados.
TRiG 22/08/19
@CompuChip como eu esperava, você está interessado em computadores :-) para reconhecer o número hexadecimal à primeira vista. Mas o Q foi escrito para não mostrar o número da fatura diretamente, para que outras pessoas saibam quantas faturas existem. Quando eu mostrar um número hexadecimal para minha esposa, mãe, vizinha ... eles não saberão o que é esse "texto estranho". Se houver um aviso sobre problema de segurança de acordo com os números da fatura no Q, sugiro um método de hash complexo para esse fim.
Jarda
@TripeHound ele ainda pode ser capaz de pesquisar por ID internamente ou dentro de algum ponto de entrada de acesso restrito ...
Jarda
0

Se ambas as teclas estão apontando para o mesmo fato, e nunca colidiriam. Por que não derivar a outra chave da chave original usando alguma função escalar que criaria código hash personalizado da sua chave original.

Como alternativa, você pode criar uma tabela de mapeamento de anexo que armazene as duas versões da chave. esta tabela atuará como um dicionário para pesquisar a chave secundária.

De acordo com meu entendimento, chaves são índices implícitos e, quanto mais você adicionar índices, mais inserções lentas serão.

A.Rashad
fonte
+1 Sim, adicionar o que é potencialmente uma coluna de cadeia grande com um índice certamente não é a operação sem valor que outros sugerem. Sobrecarga de armazenamento à parte, à medida que os índices são adicionados, a velocidade de inserção começa a diminuir.
Robbie Dee
0

Outra abordagem para o seu caso de uso específico é que, em vez de modificar o banco de dados e o aplicativo, você pode apenas criar uma rota personalizada para as faturas, de modo que / invoices /: f (id) em que f (id) seja alguma função da identificação.

A rota personalizada é responsável por mapear uma solicitação para a ação correta do lado do servidor.


fonte
0

É uma prática totalmente aceitável, também chamada de 'Chave Alternativa' (AK). Basicamente, o AK é outro índice ou restrição exclusivos.

Você pode até criar restrições de chave estrangeira com base no seu AK.

Um possível caso de uso é o que você explicou: você tem uma PK agrupada em um número de identidade cada vez maior, mas não deseja que esse número seja exibido ou usado como critério de pesquisa, porque pode ser simplesmente adivinhado. Além disso, você tem um identificador exclusivo aleatório ou número de referência como AK, e esse é o ID que você apresenta ao usuário

Alex Schievink
fonte
0

Existem vários tipos de chaves / índices. Uma chave primária é um índice exclusivo especial e, como as respostas dizem, você certamente pode criar outra chave exclusiva. E eu concordo que é melhor não expor os dados internos do banco de dados, a menos que haja um motivo muito bom.

Como a pergunta está no contexto de faturas e números, pode valer a pena pesquisar como o setor contábil espera que os números das faturas sejam: http://smallbusiness.chron.com/assign-invoice-numbers-52422.html

Pode parecer confuso ter um ID interno que seja uma chave primária e outro campo exclusivo com o número da fatura visível do aplicativo / cliente. Mas não é tão impuro quando, digamos, um ano depois, o cliente deseja adotar um novo esquema de numeração de faturas. Nesse caso, você não perturbaria o id interno e suas relações em outras tabelas para renumerar toda a bola de cera. Você manteria seu ID interno e renumerará o número da fatura não interna.

Idealmente, você se esforça para não amarrar tabelas em chaves / chaves estrangeiras que provavelmente mudarão e mantém suas tabelas e relações internas transparentes à camada do aplicativo.

Thomas Carlisle
fonte
0

Vá em frente.

Isso não é diferente do campo "lesma" que os artigos de blog e afins costumam ter - uma maneira exclusiva de se referir ao registro do banco de dados separado da chave primária, adequado para uso em um URL. Eu nunca ouvi alguém argumentar contra isso.

RemcoGerlich
fonte