CURRENT_TIMESTAMP peut-il être utilisé comme CLÉ PRIMAIRE?

10

Peut CURRENT_TIMESTAMPêtre utilisé comme un PRIMARY KEY?

Y a-t-il une possibilité que deux ou plusieurs INSERTS différents obtiennent la même chose CURRENT_TIMESTAMP?

John Puskin
la source
3
J'ai entendu parler d'une application codée en utilisant l'horodatage comme PK, dans les années 1990. Dix ans plus tard, les PC sont devenus plus rapides et les horodatages ont été dupliqués. Cela a provoqué de très graves problèmes, car la fonctionnalité de l'application était très critique. De plus, l'unicité de PK n'a pas été correctement appliquée dans l'application.
Victor Di Leo
Existe-t-il une possibilité que deux ou plusieurs INSERT différents obtiennent le même CURRENT_TIMESTAMP? Il suffit qu'une requête insère 2 enregistrements pour collision. La réponse à une question est donc "NON".
Akina
3
Je suis curieux de savoir pourquoi vous voudriez cela?
Nanne
@Nanne Je soupçonne ceci: MySQL a une très bonne gestion des identifiants entiers incrémentés automatiquement (simplement un attribut auto_increment au champ). PostgreSQL ne l'a pas, il a un type série qui est beaucoup moins beau.
peterh

Réponses:

18

Selon la documentation , la précision des CURRENT_TIMESTAMPmicrosecondes est. Ainsi, la probabilité d'une collision est faible, mais possible.

Imaginez maintenant un bogue qui se produit très rarement et provoque des erreurs de base de données. Est-il difficile de le déboguer? C'est un bug bien pire que celui qui est au moins déterministe.

Le contexte plus large: vous voulez probablement éviter ces petites nuances avec les séquences, ce qui est particulièrement gênant si vous êtes habitué à MySQL.

De plus, si vous utilisez des transactions (la plupart des frameworks Web, en particulier Java, le font!), Les horodatages seront les mêmes à l'intérieur d'une transaction! Une démonstration:

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)

À plus? Deux sélections, exactement le même résultat. Je ne tape pas si vite. ;-)

-

Si vous voulez facilement des identifiants, en évitant l'utilisation des séquences, générez une valeur de hachage à partir des vrais identifiants des enregistrements. Par exemple, si votre base de données contient des humains et que vous savez que leur date de naissance, le nom de jeune fille de leur mère et leur vrai nom les identifient de manière unique, utilisez un

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

comme id. À côté de cela, vous pouvez utiliser une CreationDatecolonne, après ce que vous indexez la table, mais ce n'est pas une clé (qui est l'id).

Ps En général, c'est une très bonne pratique de rendre votre base de données aussi déterministe que possible. C'est-à-dire que la même opération devrait créer exactement le même changement dans la base de données . Tout ID basé sur l'horodatage ne parvient pas à cette fonctionnalité importante. Et si vous voulez déboguer ou simuler quelque chose? Vous rejouez une opération et le même objet sera créé avec un identifiant différent ... ce n'est vraiment pas difficile à suivre, et cela épargne beaucoup d'heures de travail.

Ps2 Quiconque vérifiera votre code à l'avenir, n'aura pas la meilleure opinion des identifiants générés par l'horodatage, pour les raisons ci-dessus.

peterh - Réintégrer Monica
la source
Même si vous n'utilisez pas de transactions, vous utilisez en fait des transactions (car Postgres n'a pas de mode sans transactions, il a juste un autocommit). Donc, si vous faites INSERTplusieurs lignes, elles obtiennent toutes la même chose current_timestamp. Et puis vous avez des déclencheurs ...
Kevin
2
J'ai entendu parler d'une application qui s'est cassée à cause des 2 gars qui avaient le même nom et qui sont nés le même jour et leurs noms de mère étaient identiques. Aie. Si cela PEUT arriver, ça ARRIVERA, tôt ou tard.
Balazs Gunics
@BalazsGunics Helló :-) Ce n'était qu'un exemple. Par exemple, dans des scénarios réels, je pense que l'ID comme adresse e-mail ou le nom d'utilisateur choisi (qui ne peut être enregistré que s'il n'existe pas encore) suffit. Le gouvernement a tendance à utiliser un numéro d'identification personnel, comme 1 870728 0651. L'important est que lier un identifiant à un horodatage ou à une valeur aléatoire soit une mauvaise pratique, car cela rend la base de données moins déterministe.
peterh
@BalazsGunics A côté de cela, deux personnes avec le même nom de mère + prénom + anniversaire, cela provoquerait toujours une erreur déterministe. Collision de clé primaire en raison du fait que deux transactions ayant des insertions se sont produites dans la même microseconde, il s'agit toujours d'un problème non déterministe et très difficilement reproductible.
peterh
10

Pas vraiment parce qu'il est possible pour CURRENT_TIMESTAMP de fournir deux valeurs identiques pour deux INSERTs suivants (ou un INSERT unique avec plusieurs lignes).

Utilisez plutôt un UUID basé sur le temps: uuid_generate_v1mc () .

Linas
la source
7

Strictement parlant: non . Parce que CURRENT_TIMESTAMPc'est une fonction et qu'une ou plusieurs colonnes de table peuvent former une PRIMARY KEYcontrainte.

Si vous voulez créer une PRIMARY KEYcontrainte sur une colonne avec la valeur par défaut CURRENT_TIMESTAMP, la réponse est: oui, vous le pouvez . Rien ne vous empêche de le faire, comme rien ne vous empêche de tirer des pommes de la tête de votre fils. La question n'aurait toujours pas de sens tant que vous n'en définiriez pas le but. Quels types de données les colonnes et les tables sont-elles censées contenir? Quelles règles essayez-vous de mettre en œuvre?

En règle générale, l'idée est liée à des erreurs de clé en double, car il CURRENT_TIMESTAMPs'agit d'une STABLEfonction renvoyant la même valeur pour la même transaction (l'heure de début de la transaction). Plusieurs INSERT dans la même transaction sont susceptibles de se heurter - comme d'autres réponses déjà illustrées. Le manuel:

Étant donné que ces fonctions renvoient l'heure de début de la transaction en cours, leurs valeurs ne changent pas pendant la transaction. Ceci est considéré comme une caractéristique: l'intention est de permettre à une seule transaction d'avoir une notion cohérente de l'heure «actuelle», de sorte que plusieurs modifications au sein d'une même transaction portent le même horodatage.

Les horodatages PostgreSQL sont implémentés sous forme d'entiers de 8 octets représentant jusqu'à 6 chiffres fractionnaires (résolution en microsecondes).

Si vous créez une table qui ne doit pas contenir plus d'une ligne par microseconde et que cette condition ne changera pas (quelque chose nommé sensor_reading_per_microsecond), alors cela pourrait avoir du sens. Les lignes en double sont censées déclencher une erreur de violation de clé en double. C'est une exception exotique, cependant. Et le type de données timestamptz(non timestamp) serait probablement préférable. Voir:

Je préfère toujours utiliser une clé primaire série de substitution à la place. Et ajoutez une UNIQUEcontrainte sur la colonne d'horodatage. Moins de complications possibles, sans compter sur les détails de mise en œuvre du SGBDR.

Erwin Brandstetter
la source
Même sensor_reading_per_microsecondpeut entrer en collision si vous ne pouvez pas absolument garantir que la synchronisation de chaque lecture est parfaitement synchronisée par rapport à la précédente; une déviation inférieure à la microseconde (ce qui n'est souvent pas impossible) rompt le schéma. En général, j'éviterais tout à fait cela. (Attention, comme vous l'avez indiqué, dans un tel cas, la collision résultante peut être souhaitable!)
Courses de légèreté en orbite
@Lightness: je suis entièrement d'accord. Votre exemple avec décalage temporel involontaire après une petite déviation arrondie illustre une autre mise en garde.
Erwin Brandstetter