¿Se puede usar CURRENT_TIMESTAMP como una CLAVE PRIMARIA?

10

Se CURRENT_TIMESTAMPpuede utilizar como PRIMARY KEY?

¿Existe la posibilidad de que dos o más INSERTs diferentes obtengan lo mismo CURRENT_TIMESTAMP?

John Puskin
fuente
3
Escuché de una aplicación que fue codificada usando una marca de tiempo como PK, en la década de 1990. Diez años después, las PC se hicieron más rápidas y las marcas de tiempo se duplicaron. Esto causó problemas muy serios, ya que la funcionalidad de la aplicación era muy crítica. Además, la singularidad PK no se aplicaba correctamente en toda la aplicación.
Victor Di Leo
¿Existe la posibilidad de que dos o más INSERTs diferentes obtengan el mismo CURRENT_TIMESTAMP? Es suficiente una consulta insertada 2 registros para colisión. Entonces la respuesta para una pregunta de tema es "NO".
Akina
3
Tengo curiosidad por saber por qué quieres esto.
Nanne
@Nanne Sospecho que esto: MySQL tiene un manejo muy agradable de identificadores enteros incrementados automáticamente (simplemente un atributo auto_increment para el campo). PostgreSQL no tiene, tiene un tipo de serie que es mucho menos hermoso.
peterh - Restablece a Mónica el

Respuestas:

18

Según la documentación , la precisión de los CURRENT_TIMESTAMPmicrosegundos. Por lo tanto, la probabilidad de una colisión es baja, pero posible.

Ahora imagine un error que ocurre muy raramente y causa errores en la base de datos. ¿Qué tan difícil es depurarlo? Es un error mucho peor que uno que es al menos determinista.

El contexto más amplio: probablemente desee evitar estos pequeños matices con las secuencias, lo cual es particularmente molesto si está acostumbrado a MySQL.

Además, si está utilizando transacciones (¡la mayoría de los marcos web, particularmente los Java, sí!), ¡Las marcas de tiempo serán las mismas dentro de una transacción! Una demostración:

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)

¿Nos vemos? Dos selecciones, exactamente el mismo resultado. No escribo tan rápido. ;-)

-

Si desea identificar fácilmente, evitando el uso de las secuencias, genere un valor hash a partir de los identificadores reales de los registros. Por ejemplo, si su base de datos tiene humanos, y sabe que su fecha de nacimiento, el apellido de soltera de la madre y el nombre real los identifica de manera única, entonces use un

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

como id. Además de eso, puede usar una CreationDatecolumna, después de lo que indexa la tabla, pero no es una clave (que es la identificación).

Ps En general, es una muy buena práctica hacer que su DB sea tan determinista, como sea posible. Es decir, la misma operación debería crear exactamente el mismo cambio en la base de datos . Cualquier ID basado en la marca de tiempo falla esta importante característica. ¿Qué pasa si quieres depurar o simular algo? Vuelve a reproducir una operación y se creará el mismo objeto con una identificación diferente ... realmente no es difícil de seguir y ahorra muchas horas de trabajo.

Ps2 Cualquiera que verifique su código en el futuro, no tendrá la mejor opinión al ver los identificadores generados por la marca de tiempo, por los motivos anteriores.

peterh - Restablece a Monica
fuente
Incluso si no está utilizando transacciones, de hecho está utilizando transacciones (porque Postgres no tiene un modo sin transacciones, solo tiene confirmación automática). Entonces, si haces una INSERTde varias filas, todas obtienen lo mismo current_timestamp. Y luego tienes disparadores ...
Kevin
2
He oído hablar de una aplicación que se rompió debido a que los 2 chicos tenían el mismo nombre y nacieron el mismo día y sus nombres maternos eran idénticos. Ay. Si PUEDE suceder, sucederá, tarde o temprano.
Balazs Gunics
@BalazsGunics Helló :-) Fue solo un ejemplo. Por ejemplo, en escenarios reales, creo que la identificación como dirección de correo electrónico o el nombre de usuario elegido (que puede registrarse solo si aún no existe) es suficiente. El gobierno tiende a usar algún número de identificación personal, como 1 870728 0651. Lo importante es que vincular una identificación a una marca de tiempo o un valor aleatorio es, en mi opinión, una mala práctica, ya que hace que el DB sea menos determinista.
peterh - Restablece a Mónica el
@BalazsGunics Además de eso, dos personas con el mismo nombre de madre + nombre de pila + cumpleaños, aún causaría un error determinista. La colisión de la clave primaria debido a que dos transacciones con inserciones ocurrieron en el mismo microsegundo, sigue siendo un problema no determinista y muy difícil de reproducir.
peterh - Restablece a Mónica el
10

No realmente porque es posible que CURRENT_TIMESTAMP proporcione dos valores idénticos para dos INSERTs posteriores (o un solo INSERT con varias filas).

Utilice un UUID basado en el tiempo en su lugar: uuid_generate_v1mc () .

Linas
fuente
7

Estrictamente hablando: No. Porque CURRENT_TIMESTAMPes una función y solo una o más columnas de la tabla pueden formar una PRIMARY KEYrestricción.

Si quiere crear una PRIMARY KEYrestricción en una columna con el valor predeterminado CURRENT_TIMESTAMP, la respuesta es: Sí, puede hacerlo . Nada le impide hacerlo, como nada le impide disparar manzanas de la cabeza de su hijo. La pregunta aún no tendría sentido mientras no definas el propósito de la misma. ¿Qué tipo de datos deben contener la columna y la tabla? ¿Qué reglas estás tratando de implementar?

Por lo general, la idea está destinada a errores de clave duplicados, ya que CURRENT_TIMESTAMPes una STABLEfunción que devuelve el mismo valor para la misma transacción (la hora de inicio de la transacción). Múltiples INSERTs en la misma transacción están obligados a colisionar, como otras respuestas ya ilustradas. El manual:

Dado que estas funciones devuelven la hora de inicio de la transacción actual, sus valores no cambian durante la transacción. Esto se considera una característica: la intención es permitir que una sola transacción tenga una noción coherente del tiempo "actual", de modo que múltiples modificaciones dentro de la misma transacción tengan la misma marca de tiempo.

Las marcas de tiempo de Postgres se implementan como enteros de 8 bytes que representan hasta 6 dígitos fraccionarios (resolución de microsegundos).

Si está creando una tabla que se supone que no debe contener más de una fila por microsegundo y esa condición no va a cambiar (algo llamado sensor_reading_per_microsecond), entonces podría tener sentido. Se supone que las filas duplicadas generan un error de violación de clave duplicada. Sin embargo, esa es una excepción exótica. Y el tipo de datos timestamptz(no timestamp) probablemente sería preferible. Ver:

Todavía preferiría usar una clave primaria serial sustituta en su lugar. Y agregue una UNIQUErestricción en la columna de marca de tiempo. Menos complicaciones posibles, sin depender de los detalles de implementación del RDBMS.

Erwin Brandstetter
fuente
Incluso sensor_reading_per_microsecondpuede colisionar si no puede garantizar absolutamente que el tiempo de cada lectura esté perfectamente sincronizado con respecto al anterior; una desviación de menos de microsegundos (que a menudo no es imposible) rompe el esquema. En general, todavía evitaría esto por completo. (¡Ojo, como has indicado, en tal caso, la colisión resultante puede ser deseable!)
ligereza corre en órbita el
@Lightness: estoy totalmente de acuerdo. Su ejemplo con el cambio de tiempo no deseado después de una pequeña desviación redondeada ilustra otra advertencia.
Erwin Brandstetter