CURRENT_TIMESTAMP pode ser usado como uma PRIMARY KEY?

10

Pode CURRENT_TIMESTAMPser usado como um PRIMARY KEY?

Existe a possibilidade de dois ou mais INSERTs diferentes obterem o mesmo CURRENT_TIMESTAMP?

John Puskin
fonte
3
Ouvi falar de um aplicativo que foi codificado usando carimbo de data e hora como PK, nos anos 90. Dez anos depois, os PCs se tornaram mais rápidos e os timestamps foram duplicados. Isso causou problemas muito sérios, pois a funcionalidade do aplicativo era altamente crítica. Além disso, a exclusividade da PK não foi aplicada corretamente em todo o aplicativo.
Victor Di Leo
Existe a possibilidade de dois ou mais INSERTs diferentes obterem o mesmo CURRENT_TIMESTAMP? Basta uma consulta inserida 2 registros para colisão. Portanto, a resposta para uma pergunta do assunto é "NÃO".
Akina
3
Estou curioso para saber por que você quer isso?
7118 Nanne
@ Anne: Eu suspeito que isso: o MySQL possui uma manipulação muito agradável de IDs inteiros incrementados automaticamente (simplesmente um atributo auto_increment no campo). O PostgreSQL não possui, possui um tipo de série muito menos bonito.
peterh - Restabelece Monica

Respostas:

18

Conforme a documentação , a precisão do CURRENT_TIMESTAMPé microssegundos. Assim, a probabilidade de uma colisão é baixa, mas possível.

Agora imagine um bug que acontece muito raramente e causa erros no banco de dados. Quão difícil é depurá-lo? É um bug muito pior do que um que é pelo menos determinístico.

O contexto mais amplo: você provavelmente deseja evitar essas pequenas nuances com as seqüências, o que é particularmente irritante se você está acostumado ao MySQL.

Além disso, se você estiver usando transações (a maioria das estruturas da Web, principalmente as de Java, fazem!), Os carimbos de data e hora serão os mesmos dentro de uma transação! Uma demonstração:

postgres=# begin;
BEGIN
postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

Até logo? Duas seleções, exatamente o mesmo resultado. Eu não digito tão rápido. ;-)

-

Se você quiser IDs com facilidade, evitando o uso das seqüências, gere algum valor de hash a partir dos identificadores reais dos registros. Por exemplo, se seu banco de dados possui seres humanos e você sabe que a data de nascimento, o nome de solteira da mãe e o nome real os identificam exclusivamente, use um

md5(mother_name || '-' || given_name || '-' birthday);

como id. Além disso, você pode usar uma CreationDatecoluna, depois do que indexa a tabela, mas não é uma chave (que é o ID).

Ps Em geral, é uma prática muito boa tornar seu banco de dados tão determinístico quanto possível. Ou seja, a mesma operação deve criar exatamente a mesma alteração no banco de dados . Qualquer identificação baseada em carimbo de data e hora falha neste recurso importante. E se você quiser depurar ou simular alguma coisa? Você reproduz uma operação e o mesmo objeto será criado com um ID diferente ... não é realmente difícil de seguir e poupa muitas horas de trabalho.

Ps2 Qualquer pessoa que verifique seu código no futuro não terá a melhor opinião para ver os IDs gerados por carimbo de data / hora, pelos motivos acima.

peterh - Restabelecer Monica
fonte
Mesmo que você não esteja usando transações, na verdade você está usando transações (porque o Postgres não possui um modo sem transações, apenas com confirmação automática). Portanto, se você fizer uma INSERTde várias linhas, todas elas serão iguais current_timestamp. E então você tem gatilhos ...
Kevin
2
Eu ouvi falar de um aplicativo que quebrou por causa dos dois caras que tinham o mesmo nome e nasceram no mesmo dia e o nome da mãe deles era idêntico. Ai. Se PODE acontecer, acontecerá, mais cedo ou mais tarde.
Balazs Gunics
@BalazsGunics Helló :-) Foi apenas um exemplo. Por exemplo, em cenários reais, acho que a identificação como endereço de email ou o nome de usuário escolhido (que pode ser registrado apenas se ainda não existir) é suficiente. O governo tende a usar algum número de identificação pessoal, como 1 870728 0651. O importante é que vincular um ID a um carimbo de data / hora ou a um valor aleatório é uma prática ruim, pois torna o banco de dados menos determinístico.
peterh - Restabelece Monica
@BalazsGunics Além disso, duas pessoas com o mesmo nome-mãe + nome_nome + aniversário, causariam ainda um erro determinístico. A colisão de chave primária devido ao fato de duas transações terem inserções ocorrerem no mesmo microssegundo, ainda é um problema não determinístico e muito dificilmente reproduzível.
peterh - Restabelece Monica
10

Na verdade, não é porque é possível que CURRENT_TIMESTAMP forneça dois valores idênticos para dois INSERTs subsequentes (ou um único INSERT com várias linhas).

Use um UUID baseado em tempo: uuid_generate_v1mc () .

Linas
fonte
7

Estritamente falando: Não. Porque CURRENT_TIMESTAMPé uma função e apenas uma ou mais colunas da tabela podem formar uma PRIMARY KEYrestrição.

Se você deseja criar uma PRIMARY KEYrestrição em uma coluna com o valor padrão CURRENT_TIMESTAMP, a resposta é: Sim, você pode . Nada o impede de fazê-lo, como nada o impede de atirar maçãs da cabeça do seu filho. A questão ainda não faria sentido enquanto você não definir seu objetivo. Que tipo de dados a coluna e a tabela devem conter? Quais regras você está tentando implementar?

Normalmente, a ideia é executada com erros de chave duplicados, pois CURRENT_TIMESTAMPé uma STABLEfunção que retorna o mesmo valor para a mesma transação (a hora de início da transação). Vários INSERTs na mesma transação são obrigados a colidir - como outras respostas já ilustradas. O manual:

Como essas funções retornam a hora de início da transação atual, seus valores não mudam durante a transação. Isso é considerado um recurso: a intenção é permitir que uma única transação tenha uma noção consistente do horário "atual", para que várias modificações na mesma transação tenham o mesmo carimbo de hora.

Os carimbos de data e hora do Postgres são implementados como números inteiros de 8 bytes, representando até 6 dígitos fracionários (resolução de microssegundos).

Se você está construindo uma tabela que é suposto não detêm mais de uma linha por microssegundo e essa condição não vai mudar (algo chamado sensor_reading_per_microsecond), então pode fazer sentido. Linhas duplicadas devem gerar um erro de violação de chave duplicada. Essa é uma exceção exótica, no entanto. E o tipo de dados timestamptz(não timestamp) provavelmente seria preferível. Vejo:

Eu ainda preferiria usar uma chave primária serial substituta. E adicione uma UNIQUErestrição na coluna de carimbo de data / hora. Menos complicações possíveis, sem depender dos detalhes de implementação do RDBMS.

Erwin Brandstetter
fonte
A Even sensor_reading_per_microsecondpode colidir se você não puder garantir absolutamente que o tempo de cada leitura esteja perfeitamente sincronizado em relação à anterior; um desvio de microssegundo (que geralmente não é impossível) interrompe o esquema. Em geral, eu ainda evitaria isso totalmente. (Lembre-se, como você indicou, nesse caso, a colisão resultante pode ser desejável!)
Lightness Races in Orbit em
@ Lightness: estou de pleno acordo. Seu exemplo com mudança de tempo não intencional após um pequeno desvio arredondado ilustra outra advertência.
Erwin Brandstetter