Avoir à la fois une colonne d'horodatage créée et mise à jour dans MySQL 4.0

127

J'ai le schéma de table suivant;

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;

Il échoue avec l'erreur suivante:

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

Ma question est la suivante: puis-je avoir ces deux champs? ou dois-je définir manuellement un champ LastUpdated lors de chaque transaction?

Xenph Yan
la source
Hé, @Xenph Yan, cela vous dérangerait-il de changer la réponse "Accepté" de la mauvaise réponse actuelle à la bonne? Cette mauvaise réponse acceptée m'a fait perdre environ 15 minutes à essayer de comprendre ce qui se passait ...
Bruno Reis
1
@BrunoReis Done, merci pour le ramassage.
Xenph Yan

Réponses:

128

À partir de la documentation MySQL 5.5 :

Une colonne TIMESTAMP dans une table peut avoir l'horodatage actuel comme valeur par défaut pour l'initialisation de la colonne, comme valeur de mise à jour automatique, ou les deux. Il n'est pas possible que l'horodatage actuel soit la valeur par défaut pour une colonne et la valeur de mise à jour automatique pour une autre colonne.

Changements dans MySQL 5.6.5 :

Auparavant, au plus une colonne TIMESTAMP par table pouvait être automatiquement initialisée ou mise à jour à la date et à l'heure actuelles. Cette restriction a été levée. Toute définition de colonne TIMESTAMP peut avoir n'importe quelle combinaison de clauses DEFAULT CURRENT_TIMESTAMP et ON UPDATE CURRENT_TIMESTAMP. De plus, ces clauses peuvent désormais être utilisées avec les définitions de colonne DATETIME. Pour plus d'informations, consultez Initialisation et mise à jour automatiques pour TIMESTAMP et DATETIME.

Robert Gamble
la source
Cela a changé (au moins dans MySQL 5.0). Voir la documentation: dev.mysql.com/doc/refman/5.0/en/timestamp.html
SimonSimCity
Contrer @SimonSimCity: La documentation pour 5.0 dit exactement la même chose que ci-dessus.
davemyron
5
@RobertGamble Je pense que la question la plus intéressante serait POURQUOI LE HECK NE FAIT PAS, ils n'ont pas fourni un moyen de faire cette fonctionnalité très couramment demandée / nécessaire.
Ray
22
Cela semble un peu arbitraire de la part de MySQL. Quelqu'un peut-il expliquer pourquoi c'est le cas?
humble_coder
12
Commentaire vraiment très vieux, MAIS, il a finalement été changé: 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

Il y a une astuce pour avoir les deux horodatages, mais avec une petite limitation.

Vous ne pouvez utiliser qu'une seule des définitions dans une table. Créez les deux colonnes d'horodatage comme ceci:

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() 
); 

Notez qu'il est nécessaire d'entrer nulldans les deux colonnes pendant 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
la source
Voici un lien de la documentation MySQL qui décrit cette fonction: dev.mysql.com/doc/refman/5.0/en/timestamp.html
SimonSimCity
6
la première boîte de code, une fois entrée manuellement, fonctionnait parfaitement pour moi. Merci! J'aurais aimé avoir assez de «karma» pour voter pour cette réponse car elle a sauvé mon bacon. mmmmm sauvé du bacon.
amatusko
Vous devez vous assurer que ce stamp_created paramètre est également défini sur NOT NULL. MySQL remplacera automatiquement le NULL par l'horodatage actuel.
Valentin Despa
Pourquoi ne faites-vous pas également le stamp_createdchamp comme: stamp_created timestamp default now()au lieu d'utiliser une valeur «ZERO»?
Sebastian Scholle
28

Vous pouvez les avoir tous les deux, il suffit de décoller l'indicateur "CURRENT_TIMESTAMP" sur le champ créé. Chaque fois que vous créez un nouvel enregistrement dans la table, utilisez simplement "NOW ()" pour une valeur.

Ou.

Au contraire, supprimez l'indicateur «ON UPDATE CURRENT_TIMESTAMP» et envoyez le NOW () pour ce champ. De cette façon, cela a plus de sens.

Stephen Walcher
la source
2
Est-ce le seul moyen? Je ne peux pas laisser la base de données s'occuper de tous ces détails?
Xenph Yan
2
Selon le manuel MySql, CURRENT_TIMESTAMP est un synonyme de NOW () donc je ne pense pas que cela fonctionnera.
tvanfosson
Je suis sûr que vous pouvez, c'est juste que CURRENT_TIMESTAMP est un indicateur réservé à un seul champ. Quoi qu'il en soit, si vous aviez cet indicateur là-dedans, quelle que soit la valeur que vous avez pour ce champ lorsque vous ajoutez un enregistrement, ce sera toujours l'horodatage actuel, d'où le nom.
Stephen Walcher
1
@tvanfosson: L'utilisation de 'NOW ()' dans une requête transmet l'heure actuelle à la base de données. La valeur réelle dépend de la langue, du moteur et probablement d'une centaine d'autres choses que je ne sais pas. L'indicateur auquel je faisais référence le définit de sorte que lorsqu'un enregistrement est créé, l'heure est ajoutée à ce champ.
Stephen Walcher
2
J'ai opté pour la méthode insert NOW (), principalement car d'autres codes pourraient toucher ces tables et je ne fais pas confiance aux gens pour les mettre à jour correctement. :)
Xenph Yan
26

Si vous décidez que MySQL gère la mise à jour des horodatages, vous pouvez configurer un déclencheur pour mettre à jour le champ lors de l'insertion.

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

Référence MySQL: http://dev.mysql.com/doc/refman/5.0/en/triggers.html

webkraller
la source
C'est bien ce que je cherchais. Je ne veux pas avoir à compter sur les personnes ajoutées MAINTENANT () dans la requête!
Bot
en fait, il serait préférable de le faire après l'insertion et de définir NEW. <timestamp_field> = new. <timestamp_field qui doit avoir le current_timestamp par défaut> de cette façon les deux champs sont
cohérents
@KacieHouser La mise à jour de la NOUVELLE ligne n'est pas autorisée après le déclenchement
Thomas
23

Voici comment avoir des champs createDate / lastModified automatiques et flexibles à l'aide de déclencheurs:

Définissez-les d'abord comme ceci:

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`),
)

Ajoutez ensuite ces déclencheurs:

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 vous insérez sans spécifier createDate ou lastModified, ils seront égaux et définis sur l'horodatage actuel.
  • Si vous les mettez à jour sans spécifier createDate ou lastModified, lastModified sera défini sur l'horodatage actuel.

Mais voici la belle partie:

  • Si vous insérez , vous pouvez spécifier un createDate plus ancien que l'horodatage actuel , permettant ainsi aux importations d'anciennes périodes de bien fonctionner (lastModified sera égal à createDate).
  • Si vous mettez à jour , vous pouvez spécifier un lastModified plus ancien que la valeur précédente ('0000-00-00 00:00:00' fonctionne bien), permettant de mettre à jour une entrée si vous faites des modifications cosmétiques (correction d'une faute de frappe dans un commentaire ) et vous souhaitez conserver l'ancienne date lastModified . Cela ne modifiera pas la date de la dernière modification.
extraterrestre
la source
21

Depuis MySQL 5.6, c'est facile ... essayez-le:

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
la source
Probablement une excellente solution Ce que je recherche un problème de temps de création et de mise à jour sans déclencheur
matinict
Je ne peux pas croire qu'il ait fallu autant de temps pour que cette petite nuisance soit résolue. Je n'avais même pas réalisé qu'ils avaient corrigé cela et nous sommes en 2018 ... Merci.
hsanders
4

Ce problème semble avoir été résolu dans MySQL 5.6. J'ai remarqué cela jusqu'à MySQL 5.5; voici un exemple de code:

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;

L'exécuter sur MySQL 5.5 donne:

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

Exécuter ceci sur MySQL 5.6

0 row(s) affected   0.093 sec
Kingz
la source
2

Pour mysql 5.7.21, j'utilise ce qui suit et fonctionne très bien:

CREATE TABLE Posts( modified_athorodatage NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, created_athorodatage NOT NULL DEFAULT CURRENT_TIMESTAMP)

Ioannis Chrysochos
la source
1

je pense que c'est la meilleure requête pour stamp_created et 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() 
); 

car lorsque l'enregistrement créé, stamp_createddoit être rempli par now()et stamp_updateddoit être rempli par'0000-00-00 00:00:00'

Anonim-
la source