CURRENT_TIMESTAMP può essere utilizzato come PRIMARY KEY?


10

Può CURRENT_TIMESTAMPessere usato come PRIMARY KEY?

Esiste la possibilità che due o più INSERTI diversi ottengano lo stesso CURRENT_TIMESTAMP?


3
Ho sentito parlare di un'app che è stata codificata usando il timestamp come PK, negli anni '90. Dieci anni dopo i PC sono diventati più veloci e i timestamp sono stati duplicati. Ciò ha causato problemi molto gravi, in quanto la funzionalità dell'app era estremamente critica. Inoltre, l'unicità di PK non è stata applicata correttamente in tutta l'app.
Victor Di Leo,

Esiste la possibilità che due o più INSERT diversi ottengano lo stesso CURRENT_TIMESTAMP? Basta una query per inserire 2 record per la collisione. Quindi la risposta a una domanda del soggetto è "NO".
Akina,


3
Sono curioso di sapere perché lo vorresti?
Nanne,

@Nanne Lo sospetto: MySQL ha un'ottima gestione degli ID interi incrementati automaticamente (semplicemente un attributo auto_increment al campo). PostgreSQL non ha, ha un tipo seriale che è molto meno bello.
Peter - Ripristina Monica il

Risposte:


18

Secondo la documentazione , la precisione CURRENT_TIMESTAMPè di microsecondi. Pertanto, la probabilità di una collisione è bassa, ma possibile.

Ora immagina un bug che si verifica molto raramente e causa errori del database. Quanto è difficile eseguirne il debug? È un bug molto peggiore di uno che è almeno deterministico.

Il contesto più ampio: probabilmente vorrai evitare queste piccole sfumature con le sequenze, il che è particolarmente fastidioso se sei abituato a MySQL.

Inoltre, se si utilizzano transazioni (la maggior parte dei framework Web, in particolare quelli Java, lo fanno!), I timestamp saranno gli stessi all'interno di una transazione! Una dimostrazione:

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)

Ci vediamo? Due selezioni, esattamente lo stesso risultato. Non scrivo così velocemente. ;-)

-

Se vuoi facilmente ID, evitando l'uso delle sequenze, quindi genera un valore hash dagli identificatori reali dei record. Ad esempio, se il tuo database ha umani e sai che la loro data di nascita, il nome da nubile della madre e il vero nome li identificano in modo univoco, quindi usa un

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

come id. Inoltre, puoi usare una CreationDatecolonna, dopo ciò che indicizzi la tabella, ma non è una chiave (che è l'id).

Ps In generale, è una buona pratica rendere il tuo DB così deterministico, per quanto possibile. Cioè la stessa operazione dovrebbe creare esattamente la stessa modifica nel DB . Qualsiasi ID basato su data e ora non riesce questa importante funzione. Cosa succede se si desidera eseguire il debug o simulare qualcosa? Ripeti un'operazione e lo stesso oggetto verrà creato con un ID diverso ... in realtà non è difficile da seguire e risparmia molte ore di lavoro.

Ps2 Chiunque controlla il tuo codice in futuro, non avrà la migliore opinione di vedere ID generati da timestamp, sui motivi sopra.


Anche se non stai utilizzando le transazioni, stai effettivamente utilizzando le transazioni (poiché Postgres non ha una modalità senza transazioni, ha solo l'autocommit). Quindi, se fai una INSERTdi più righe, tutte ottengono la stessa cosa current_timestamp. E poi hai i trigger ...
Kevin

2
Ho sentito parlare di un'app che si è rotta a causa dei 2 ragazzi con lo stesso nome e nati lo stesso giorno e i loro nomi di madre erano identici. Ahia. Se può succedere, accadrà, prima o poi.
Balazs Gunics,

@BalazsGunics Helló :-) Era solo un esempio. Ad esempio, in scenari reali, penso che id come indirizzo e-mail o il nome utente scelto (che può essere registrato solo se non esiste ancora) è sufficiente. Il governo tende a utilizzare un numero di identificazione personale, come 1 870728 0651. L'importante è che associare un ID a un timestamp o a un valore casuale sia una cattiva pratica, perché rende il DB meno deterministico.
Peter - Ripristina Monica il

@BalazsGunics Oltre a ciò, due persone con lo stesso nome_madre + nome_messo + compleanno, causerebbe ancora un errore deterministico. La collisione della chiave primaria dovuta al fatto che due transazioni con inserimenti sono avvenute nello stesso microsecondo, è ancora un problema non deterministico e difficilmente riproducibile.
Peter - Ripristina Monica il

10

Non proprio perché è possibile che CURRENT_TIMESTAMP fornisca due valori identici per due INSERT successivi (o un singolo INSERT con più righe).

Utilizzare invece un UUID basato sul tempo: uuid_generate_v1mc () .


7

A rigor di termini: No. Perché CURRENT_TIMESTAMPè una funzione e solo una o più colonne della tabella possono costituire un PRIMARY KEYvincolo.

Se intendi creare un PRIMARY KEYvincolo su una colonna con il valore predefinito CURRENT_TIMESTAMP, la risposta è: Sì, puoi . Niente ti impedisce di farlo, come niente ti impedisce di sparare mele dalla testa di tuo figlio. La domanda non avrebbe ancora senso anche se non ne definisci lo scopo. Che tipo di dati dovrebbero contenere la colonna e la tabella? Quali regole stai cercando di implementare?

In genere, l'idea è destinata a incorrere in errori chiave duplicati poiché CURRENT_TIMESTAMPè una STABLEfunzione che restituisce lo stesso valore per la stessa transazione (l'ora di inizio della transazione). Più INSERTI nella stessa transazione sono destinati a scontrarsi, come altre risposte già illustrate. Il manuale:

Poiché queste funzioni restituiscono l'ora di inizio della transazione corrente, i loro valori non cambiano durante la transazione. Questa è considerata una caratteristica: l'intenzione è quella di consentire a una singola transazione di avere una nozione coerente del tempo "corrente", in modo che più modifiche all'interno della stessa transazione rechino lo stesso timestamp.

I timestamp di Postgres sono implementati come numeri interi a 8 byte che rappresentano fino a 6 cifre frazionarie (risoluzione di microsecondi).

Se stai costruendo una tabella che dovrebbe contenere non più di una riga per microsecondo e che la condizione non cambierà (qualcosa chiamato sensor_reading_per_microsecond), allora potrebbe avere senso. Le righe duplicate dovrebbero generare un errore di violazione della chiave duplicata. Questa è un'eccezione esotica, però. E il tipo di dati timestamptz(non timestamp) sarebbe probabilmente preferibile. Vedere:

Preferirei comunque utilizzare una chiave primaria seriale surrogata. E aggiungi un UNIQUEvincolo nella colonna timestamp. Meno complicazioni possibili, non basandosi sui dettagli di implementazione di RDBMS.


Anche sensor_reading_per_microsecondpuò collidere se non può assolutamente garantire che i tempi di ogni lettura è perfettamente sincronizzato rispetto alla precedente; una deviazione al di sotto del microsecondo (che spesso non è impossibile) rompe lo schema. In generale, lo eviterei ancora del tutto. (Intendiamoci, come hai indicato, in tal caso, la collisione risultante potrebbe essere desiderabile!)
Razze di leggerezza in orbita

@Lightness: sono in completo accordo. Il tuo esempio con spostamento temporale involontario dopo una piccola deviazione arrotondata illustra un altro avvertimento.
Erwin Brandstetter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.