Tener una columna de marca de tiempo creada y una última actualización en MySQL 4.0

127

Tengo el siguiente esquema de tabla;

CREATE TABLE `db1`.`sms_queue` (
  `Id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `Message` VARCHAR(160) NOT NULL DEFAULT 'Unknown Message Error',
  `CurrentState` VARCHAR(10) NOT NULL DEFAULT 'None',
  `Phone` VARCHAR(14) DEFAULT NULL,
  `Created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `LastUpdated` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP,
  `TriesLeft` tinyint NOT NULL DEFAULT 3,
  PRIMARY KEY (`Id`)
)
ENGINE = InnoDB;

Falla con el siguiente error:

ERROR 1293 (HY000): Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause.

Mi pregunta es, ¿puedo tener ambos campos? ¿o tengo que configurar manualmente un campo LastUpdated durante cada transacción?

Xenph Yan
fuente
Hola, @Xenph Yan, ¿te importaría cambiar la respuesta "Aceptada" de la incorrecta actual a la correcta? Esta respuesta aceptada y incorrecta me hizo perder unos 15 minutos tratando de descubrir qué estaba pasando ...
Bruno Reis
1
@BrunoReis Hecho, gracias por la recogida.
Xenph Yan

Respuestas:

128

De la documentación de MySQL 5.5 :

Una columna TIMESTAMP en una tabla puede tener la marca de tiempo actual como el valor predeterminado para inicializar la columna, como el valor de actualización automática, o ambos. No es posible que la marca de tiempo actual sea el valor predeterminado para una columna y el valor de actualización automática para otra columna.

Cambios en MySQL 5.6.5 :

Anteriormente, como máximo una columna TIMESTAMP por tabla podía inicializarse o actualizarse automáticamente a la fecha y hora actuales. Esta restricción ha sido levantada. Cualquier definición de columna TIMESTAMP puede tener cualquier combinación de cláusulas DEFAULT CURRENT_TIMESTAMP y ON UPDATE CURRENT_TIMESTAMP. Además, estas cláusulas ahora se pueden usar con las definiciones de columna DATETIME. Para obtener más información, consulte Inicialización y actualización automáticas para TIMESTAMP y DATETIME.

Robert Gamble
fuente
Eso ha cambiado (al menos en MySQL 5.0). Consulte la documentación: dev.mysql.com/doc/refman/5.0/en/timestamp.html
SimonSimCity
Contrarrestando @SimonSimCity: Los documentos para 5.0 dicen exactamente lo mismo que arriba.
davemyron
55
@RobertGamble Creo que la pregunta más interesante sería ¿POR QUÉ EL HECK NO HAYA proporcionado una forma de hacer esta funcionalidad tan solicitada / necesaria?
Ray
22
Esto parece un poco arbitrario por parte de MySQL. ¿Alguien puede explicar por qué este es el caso?
humble_coder
12
Comentario muy, muy viejo, PERO, finalmente se cambió: Changes in MySQL 5.6.5 (2012-04-10, Milestone 8) Previously, at most one TIMESTAMP column per table could be automatically initialized or updated to the current date and time. This restriction has been lifted. Any TIMESTAMP column definition can have any combination of DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP clauses. In addition, these clauses now can be used with DATETIME column definitions. For more information, see Automatic Initialization and Updating for TIMESTAMP and DATETIME.
Mauricio Vargas
83

Hay un truco para tener ambas marcas de tiempo, pero con una pequeña limitación.

Puede usar solo una de las definiciones en una tabla. Cree ambas columnas de marca de tiempo de la siguiente manera:

create table test_table( 
  id integer not null auto_increment primary key, 
  stamp_created timestamp default '0000-00-00 00:00:00', 
  stamp_updated timestamp default now() on update now() 
); 

Tenga en cuenta que es necesario ingresar nullen ambas columnas durante insert:

mysql> insert into test_table(stamp_created, stamp_updated) values(null, null); 
Query OK, 1 row affected (0.06 sec)

mysql> select * from test_table; 
+----+---------------------+---------------------+ 
| id | stamp_created       | stamp_updated       |
+----+---------------------+---------------------+
|  2 | 2009-04-30 09:44:35 | 2009-04-30 09:44:35 |
+----+---------------------+---------------------+
2 rows in set (0.00 sec)  

mysql> update test_table set id = 3 where id = 2; 
Query OK, 1 row affected (0.05 sec) Rows matched: 1  Changed: 1  Warnings: 0  

mysql> select * from test_table;
+----+---------------------+---------------------+
| id | stamp_created       | stamp_updated       | 
+----+---------------------+---------------------+ 
|  3 | 2009-04-30 09:44:35 | 2009-04-30 09:46:59 | 
+----+---------------------+---------------------+ 
2 rows in set (0.00 sec)  
Bogdan Gusiev
fuente
Aquí hay un enlace desde el MySQL-Documentación que describe esa función: dev.mysql.com/doc/refman/5.0/en/timestamp.html
SimonSimCity
66
el primer cuadro de código, cuando se ingresó manualmente, funcionó perfectamente para mí. ¡Gracias! Desearía tener suficiente 'karma' para votar esta respuesta, ya que salvó mi tocino. mmmmm tocino guardado.
amatusko
Debe asegurarse de que stamp_created también esté configurado como NO NULO. MySQL reemplazará automáticamente el NULL con la marca de tiempo actual.
Valentin Despa
¿Por qué no haces también el stamp_createdcampo como: en stamp_created timestamp default now()lugar de usar un valor 'CERO'?
Sebastian Scholle
28

Puede tenerlos a ambos, simplemente quite la bandera "CURRENT_TIMESTAMP" en el campo creado. Siempre que cree un nuevo registro en la tabla, simplemente use "NOW ()" para obtener un valor.

O.

Por el contrario, elimine el indicador 'ON UPDATE CURRENT_TIMESTAMP' y envíe el NOW () para ese campo. Esa forma en realidad tiene más sentido.

Stephen Walcher
fuente
2
¿Es esta la única forma? ¿No puedo hacer que la base de datos se ocupe de todos esos detalles?
Xenph Yan
2
Según el manual de MySql, CURRENT_TIMESTAMP es un sinónimo de NOW (), así que no creo que esto funcione.
tvanfosson el
Estoy seguro de que puede, es solo que CURRENT_TIMESTAMP es una bandera reservada para un solo campo. De cualquier manera, si tenía esa bandera allí, independientemente del valor que tenga para ese campo cuando agrega un registro, siempre será la marca de tiempo actual, de ahí el nombre.
Stephen Walcher, el
1
@tvanfosson: el uso de 'NOW ()' en una consulta pasa la hora actual a la base de datos. Lo que ese valor realmente depende del idioma, el motor y probablemente otras cien cosas que no sé. El indicador al que me refería lo establece de modo que cuando se crea un registro, el tiempo se agrega a ese campo.
Stephen Walcher, el
2
Seguí el método insert NOW (), principalmente porque otro código podría tocar estas tablas y no confío en que las personas las actualicen correctamente. :)
Xenph Yan
26

Si decide que MySQL maneje la actualización de las marcas de tiempo, puede configurar un activador para actualizar el campo en la inserción.

CREATE TRIGGER <trigger_name> BEFORE INSERT ON <table_name> FOR EACH ROW SET NEW.<timestamp_field> = CURRENT_TIMESTAMP;

Referencia de MySQL: http://dev.mysql.com/doc/refman/5.0/en/triggers.html

webkraller
fuente
Bien, esto era lo que estaba buscando. ¡No quiero tener que confiar en las personas agregadas NOW () en la consulta!
Bot
En realidad, sería mejor que hacer después de insertar y configurar NUEVO <timestamp_field> = new <timestamp_field que realmente tiene que tener el valor predeterminado current_timestamp> esta manera ambos campos son constante..
KacieHouser
@KacieHouser La actualización de NUEVA fila no está permitida después del disparador
Thomas
23

Así es como puede tener campos createDate / lastModified automáticos y flexibles usando disparadores:

Primero defínalos así:

CREATE TABLE `entity` (
  `entityid` int(11) NOT NULL AUTO_INCREMENT,
  `createDate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `lastModified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `name` varchar(255) DEFAULT NULL,
  `comment` text,
  PRIMARY KEY (`entityid`),
)

Luego agregue estos desencadenantes:

DELIMITER ;;
CREATE trigger entityinsert BEFORE INSERT ON entity FOR EACH ROW BEGIN SET NEW.createDate=IF(ISNULL(NEW.createDate) OR NEW.createDate='0000-00-00 00:00:00', CURRENT_TIMESTAMP, IF(NEW.createDate<CURRENT_TIMESTAMP, NEW.createDate, CURRENT_TIMESTAMP));SET NEW.lastModified=NEW.createDate; END;;
DELIMITER ;
CREATE trigger entityupdate BEFORE UPDATE ON entity FOR EACH ROW SET NEW.lastModified=IF(NEW.lastModified<OLD.lastModified, OLD.lastModified, CURRENT_TIMESTAMP);
  • Si inserta sin especificar createDate o lastModified, serán iguales y se establecerán en la marca de tiempo actual.
  • Si los actualiza sin especificar createDate o lastModified, lastModified se establecerá en la marca de tiempo actual.

Pero aquí está la buena parte:

  • Si inserta , puede especificar un createDate anterior a la marca de tiempo actual , lo que permite que las importaciones de tiempos anteriores funcionen bien (lastModified será igual a createDate).
  • Si actualiza , puede especificar un LastModified anterior al valor anterior ('0000-00-00 00:00:00' funciona bien), lo que permite actualizar una entrada si está haciendo cambios cosméticos (arreglando un error tipográfico en un comentario ) y desea mantener la fecha anterior de LastModified . Esto no modificará la última fecha modificada.
extraterrestre
fuente
21

A partir de MySQL 5.6 es muy fácil ... pruébalo:

create table tweet ( 
    id integer not null auto_increment primary key, 
    stamp_created timestamp default now(), 
    stamp_updated timestamp default now() on update now(),
    message varchar(163)
)
Shaheen Ghiassy
fuente
Probablemente una gran solución Lo que estoy buscando crea y actualiza el problema de tiempo sin disparador
matinict
No puedo creer que haya tardado tanto en resolver esta pequeña molestia. Ni siquiera me di cuenta de que habían solucionado esto y es 2018 ... Gracias.
hsanders
4

Este problema parecía haberse resuelto en MySQL 5.6. He notado esto hasta MySQL 5.5; Aquí hay un código de ejemplo:

DROP TABLE IF EXISTS `provider_org_group` ;
CREATE TABLE IF NOT EXISTS `provider_org_group` (
  `id` INT NOT NULL,
  `name` VARCHAR(100) NOT NULL,
  `type` VARCHAR(100) NULL,
  `inserted` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `insert_src_ver_id` INT NULL,
  `updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
  `update_src_ver_id` INT NULL,
  `version` INT NULL,
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC),
  UNIQUE INDEX `name_UNIQUE` (`name` ASC))
ENGINE = InnoDB;

Ejecutar esto en MySQL 5.5 da:

ERROR 1293 (HY000): Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause

Ejecutando esto en MySQL 5.6

0 row(s) affected   0.093 sec
Kingz
fuente
2

Para mysql 5.7.21 utilizo lo siguiente y funciona bien:

CREATE TABLE Posts( modified_atmarca de tiempo NO NULL DEFAULT CURRENT_TIMESTAMP EN UPDATE CURRENT_TIMESTAMP, created_atmarca de tiempo NOT NULL DEFAULT CURRENT_TIMESTAMP)

Ioannis Chrysochos
fuente
1

Creo que esta es la mejor consulta para stamp_created y stamp_updated

CREATE TABLE test_table( 
    id integer not null auto_increment primary key, 
    stamp_created TIMESTAMP DEFAULT now(), 
    stamp_updated TIMESTAMP DEFAULT '0000-00-00 00:00:00' ON UPDATE now() 
); 

porque cuando se crea el registro, stamp_createddebe llenarse now()y stamp_updateddebe llenarse'0000-00-00 00:00:00'

Anonim
fuente