Kann CURRENT_TIMESTAMP als PRIMARY KEY verwendet werden?

10

Kann CURRENT_TIMESTAMPals verwendet werden PRIMARY KEY?

Gibt es eine Möglichkeit, dass zwei oder mehr verschiedene INSERTs gleich sind CURRENT_TIMESTAMP?

John Puskin
quelle
3
Ich habe in den 90er Jahren von einer App gehört, die mit einem Zeitstempel als PK codiert wurde. Zehn Jahre später wurden PCs schneller und Zeitstempel wurden dupliziert. Dies verursachte sehr schwerwiegende Probleme, da die Funktionalität der App äußerst kritisch war. Außerdem wurde die PK-Eindeutigkeit in der gesamten App nicht ordnungsgemäß erzwungen.
Victor Di Leo
Gibt es eine Möglichkeit, dass zwei oder mehr verschiedene INSERTs denselben CURRENT_TIMESTAMP erhalten? Es reicht aus, wenn eine Abfrage 2 Datensätze für die Kollision eingefügt hat. Die Antwort auf eine Themenfrage lautet also "NEIN".
Akina
3
Ich bin gespannt, warum du das willst.
Nanne
@Nanne Ich vermute das: MySQL hat eine sehr gute Handhabung von automatisch inkrementierten Integer-IDs (einfach ein auto_increment-Attribut für das Feld). PostgreSQL hat nicht, es hat einen seriellen Typ, der weitaus weniger schön ist.
Peterh

Antworten:

18

Gemäß der DokumentationCURRENT_TIMESTAMP beträgt die Genauigkeit der Mikrosekunden. Somit ist die Wahrscheinlichkeit einer Kollision gering, aber möglich.

Stellen Sie sich nun einen Fehler vor, der sehr selten auftritt und Datenbankfehler verursacht. Wie schwer ist es zu debuggen? Es ist ein weitaus schlimmerer Fehler als einer, der zumindest deterministisch ist.

Der breitere Kontext: Sie möchten wahrscheinlich diese kleinen Nuancen mit den Sequenzen vermeiden, was besonders ärgerlich ist, wenn Sie an MySQL gewöhnt sind.

Wenn Sie Transaktionen verwenden (die meisten Web-Frameworks, insbesondere die Java-Frameworks!), Sind die Zeitstempel innerhalb einer Transaktion identisch! Eine Demonstration:

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)

Wir sehen uns? Zwei Auswahlen, genau das gleiche Ergebnis. Ich tippe nicht so schnell. ;-);

- -

Wenn Sie leicht IDs wünschen und die Verwendung der Sequenzen vermeiden möchten, generieren Sie einen Hashwert aus den realen Bezeichnern der Datensätze. Wenn Ihre Datenbank beispielsweise Menschen enthält und Sie wissen, dass deren Geburtsdatum, der Mädchenname der Mutter und der tatsächliche Name sie eindeutig identifizieren, verwenden Sie eine

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

als id. Außerdem können Sie CreationDatenach dem Indizieren der Tabelle eine Spalte verwenden, die jedoch kein Schlüssel ist (dies ist die ID).

Ps Im Allgemeinen ist es eine sehr gute Praxis , Ihre DB so deterministisch wie möglich zu gestalten. Das heißt, der gleiche Vorgang sollte genau die gleiche Änderung in der Datenbank erzeugen . Jede zeitstempelbasierte ID schlägt diese wichtige Funktion fehl. Was ist, wenn Sie etwas debuggen oder simulieren möchten? Sie spielen einen Vorgang erneut ab und dasselbe Objekt wird mit einer anderen ID erstellt . Es ist wirklich nicht schwer zu verfolgen und spart viele Arbeitsstunden.

Ps2 Jeder, der in Zukunft Ihren Code überprüft, wird aus den oben genannten Gründen nicht die beste Meinung dazu haben, durch Zeitstempel generierte IDs zu sehen.

Peter - Setzen Sie Monica wieder ein
quelle
Selbst wenn Sie keine Transaktionen verwenden, verwenden Sie tatsächlich Transaktionen (da Postgres keinen Modus ohne Transaktionen hat, verfügt es nur über eine automatische Festschreibung). Wenn Sie also INSERTmehrere Zeilen ausführen, erhalten alle die gleichen Zeilen current_timestamp. Und dann haben Sie Auslöser ...
Kevin
2
Ich habe von einer App gehört, die kaputt ging, weil die beiden Jungs den gleichen Namen hatten und am selben Tag geboren wurden und ihre Mutternamen identisch waren. Autsch. Wenn es passieren kann, wird es früher oder später passieren.
Balazs Gunics
@ BalazsGunics Helló :-) Es war nur ein Beispiel. In realen Szenarien denke ich beispielsweise, dass eine ID als E-Mail-Adresse oder der ausgewählte Benutzername (der nur registriert werden kann, wenn er noch nicht vorhanden ist) ausreicht. Die Regierung tendiert dazu, eine persönliche Identifikationsnummer wie 1 870728 0651 zu verwenden. Wichtig ist, dass das Binden einer ID an einen Zeitstempel oder an einen zufälligen Wert imho eine schlechte Praxis ist, da dies die DB weniger deterministisch macht.
Peterh
@BalazsGunics Abgesehen davon, zwei Personen mit demselben Mutternamen + Vornamen + Geburtstag, würde dies immer noch einen deterministischen Fehler verursachen. Primärschlüsselkollision aufgrund der Tatsache, dass zwei Transaktionen mit Einfügungen in derselben Mikrosekunde stattfanden, ist dies immer noch ein nicht deterministisches und kaum reproduzierbares Problem.
Peter - Wiedereinstellung Monica
10

Nicht wirklich, weil CURRENT_TIMESTAMP zwei identische Werte für zwei nachfolgende INSERTs (oder ein einzelnes INSERT mit mehreren Zeilen) bereitstellen kann.

Verwenden Sie stattdessen eine zeitbasierte UUID: uuid_generate_v1mc () .

Linas
quelle
7

Streng genommen: No. Weil CURRENT_TIMESTAMPa Funktion und nur ein oder mehr Tabellenspalten können eine Form - PRIMARY KEYEinschränkung.

Wenn Sie eine PRIMARY KEYEinschränkung für eine Spalte mit dem Standardwert erstellen CURRENT_TIMESTAMPmöchten, lautet die Antwort: Ja, das können Sie . Nichts hält Sie davon ab, so wie nichts Sie davon abhält, Äpfel aus dem Kopf Ihres Sohnes zu schießen. Die Frage würde immer noch keinen Sinn ergeben, wenn Sie den Zweck nicht definieren. Welche Art von Daten sollen Spalte und Tabelle enthalten? Welche Regeln versuchen Sie umzusetzen?

In der Regel führt die Idee zwangsläufig zu doppelten Schlüsselfehlern, da CURRENT_TIMESTAMPeine STABLEFunktion denselben Wert für dieselbe Transaktion zurückgibt (Startzeit der Transaktion). Mehrere INSERTs in derselben Transaktion müssen wie andere bereits dargestellte Antworten kollidieren. Das Handbuch:

Da diese Funktionen die Startzeit der aktuellen Transaktion zurückgeben, ändern sich ihre Werte während der Transaktion nicht. Dies wird als Merkmal angesehen: Es ist beabsichtigt, einer einzelnen Transaktion eine konsistente Vorstellung von der „aktuellen“ Zeit zu ermöglichen, sodass mehrere Änderungen innerhalb derselben Transaktion denselben Zeitstempel tragen.

Postgres-Zeitstempel werden als 8-Byte-Ganzzahlen implementiert, die bis zu 6 Bruchziffern darstellen (Mikrosekundenauflösung).

Wenn Sie eine Tabelle erstellen, die nicht mehr als eine Zeile pro Mikrosekunde enthalten soll und sich diese Bedingung nicht ändert (etwas mit Namen sensor_reading_per_microsecond), ist dies möglicherweise sinnvoll. Doppelte Zeilen sollen einen doppelten Schlüsselverletzungsfehler auslösen. Das ist jedoch eine exotische Ausnahme. Und der Datentyp timestamptz(nicht timestamp) wäre wahrscheinlich vorzuziehen. Sehen:

Ich würde stattdessen immer noch lieber einen Ersatz-Primärschlüssel verwenden. UNIQUEFügen Sie der Zeitstempelspalte eine Einschränkung hinzu. Weniger mögliche Komplikationen, ohne auf Implementierungsdetails des RDBMS angewiesen zu sein.

Erwin Brandstetter
quelle
Auch sensor_reading_per_microsecondkollidieren können , wenn Sie nicht absolut garantieren , dass der Zeitpunkt jeder Messung perfekt in Bezug auf die vorherige synchronisiert ist; Eine Abweichung von weniger als einer Mikrosekunde (die oft nicht unmöglich ist) bricht das Schema. Im Allgemeinen würde ich das immer noch völlig vermeiden. (Wohlgemerkt, wie Sie angegeben haben, kann in einem solchen Fall die resultierende Kollision wünschenswert sein!)
Leichtigkeitsrennen im Orbit
@ Lightness: Ich bin völlig einverstanden. Ihr Beispiel mit unbeabsichtigter Zeitverschiebung nach einer gerundeten winzigen Abweichung zeigt eine weitere Einschränkung.
Erwin Brandstetter