Posiadanie zarówno utworzonych, jak i ostatnio zaktualizowanych kolumn sygnatury czasowej w MySQL 4.0

127

Mam następujący schemat tabeli;

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;

Niepowodzenie z następującym błędem:

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

Moje pytanie brzmi, czy mogę mieć oba te pola? czy też muszę ręcznie ustawić pole LastUpdated podczas każdej transakcji?

Xenph Yan
źródło
Hej, @Xenph Yan, czy mógłbyś zmienić odpowiedź „Zaakceptowano” z obecnej złej na właściwą? Ta zaakceptowana, zła odpowiedź sprawiła, że ​​straciłem około 15 minut, próbując dowiedzieć się, co się dzieje ...
Bruno Reis
1
@BrunoReis Gotowe, dzięki za odbiór.
Xenph Yan,

Odpowiedzi:

128

Z dokumentacji MySQL 5.5 :

Jedna kolumna TIMESTAMP w tabeli może mieć bieżącą sygnaturę czasową jako wartość domyślną do inicjowania kolumny, wartość automatycznej aktualizacji lub obie. Nie jest możliwe, aby bieżący znacznik czasu był wartością domyślną dla jednej kolumny, a wartością automatycznej aktualizacji dla innej kolumny.

Zmiany w MySQL 5.6.5 :

Wcześniej co najwyżej jedna kolumna TIMESTAMP na tabelę mogła zostać automatycznie zainicjowana lub zaktualizowana do bieżącej daty i godziny. To ograniczenie zostało zniesione. Każda definicja kolumny TIMESTAMP może mieć dowolną kombinację klauzul DEFAULT CURRENT_TIMESTAMP i ON UPDATE CURRENT_TIMESTAMP. Ponadto klauzule te mogą być teraz używane z definicjami kolumn typu DATETIME. Aby uzyskać więcej informacji, zobacz Automatyczne inicjowanie i aktualizowanie dla TIMESTAMP i DATETIME.

Robert Gamble
źródło
To się zmieniło (przynajmniej w MySQL 5.0). Zobacz dokumentację: dev.mysql.com/doc/refman/5.0/en/timestamp.html
SimonSimCity,
Przeciwdziałanie @SimonSimCity: Dokumentacja wersji 5.0 mówi dokładnie to samo, co powyżej.
davemyron
5
@RobertGamble Myślę, że bardziej interesującym pytaniem byłoby DLACZEGO DO DRABINY nie zapewnili sposobu na wykonanie tej bardzo często żądanej / potrzebnej funkcjonalności.
Ray
22
Wydaje się to nieco arbitralne ze strony MySQL. Czy ktoś może wyjaśnić, dlaczego tak jest?
humble_coder
12
Naprawdę bardzo stary komentarz, ALE w końcu został zmieniony: 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

Istnieje pewna sztuczka, aby mieć oba znaczniki czasu, ale z niewielkimi ograniczeniami.

Możesz użyć tylko jednej definicji w jednej tabeli. Utwórz obie kolumny ze znacznikami czasu w następujący sposób:

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

Zwróć uwagę, że konieczne jest wpisanie nulldo obu kolumn podczas 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
źródło
Oto link z dokumentacji MySQL, która opisuje tę funkcję: dev.mysql.com/doc/refman/5.0/en/timestamp.html
SimonSimCity,
6
pierwsza skrzynka kodowa po wpisaniu ręcznie działała idealnie dla mnie. Dzięki! Żałuję, że nie mam dość „karmy”, by głosować za tą odpowiedzią, ponieważ uratowała mi ona bekon. mmmmm zachowany boczek.
amatusko
Musisz się upewnić, że stamp_created jest to również ustawione jako NIE NULL. MySQL automatycznie zastąpi NULL aktualnym znacznikiem czasu.
Valentin Despa,
Dlaczego nie utworzysz stamp_createdpola jako: stamp_created timestamp default now()zamiast używać wartości „ZERO”?
Sebastian Scholle
28

Możesz mieć oba, po prostu zdejmij flagę „CURRENT_TIMESTAMP” na utworzonym polu. Za każdym razem, gdy tworzysz nowy rekord w tabeli, po prostu użyj „NOW ()” jako wartości.

Lub.

Wręcz przeciwnie, usuń flagę „ON UPDATE CURRENT_TIMESTAMP” i wyślij NOW () dla tego pola. W ten sposób ma więcej sensu.

Stephen Walcher
źródło
2
Czy to jedyny sposób? Nie mogę pozwolić, aby baza danych zadbała o wszystkie te szczegóły?
Xenph Yan
2
Zgodnie z podręcznikiem MySql, CURRENT_TIMESTAMP jest synonimem NOW (), więc nie sądzę, żeby to zadziałało.
tvanfosson
Jestem pewien, że możesz, po prostu CURRENT_TIMESTAMP to flaga zarezerwowana tylko dla jednego pola. Tak czy inaczej, gdybyś miał tam tę flagę, niezależnie od tego, jaką wartość masz dla tego pola podczas dodawania rekordu, zawsze będzie to bieżący znacznik czasu, stąd nazwa.
Stephen Walcher
1
@tvanfosson: Użycie „NOW ()” w zapytaniu przekazuje aktualny czas do bazy danych. Jaka jest ta wartość, zależy od języka, silnika i prawdopodobnie stu innych rzeczy, których nie wiem. Flaga, do której się odnosiłem, ustawia go tak, że podczas tworzenia rekordu czas jest dodawany do tego pola.
Stephen Walcher
2
Poszedłem z metodą wstawiania TERAZ (), głównie dlatego, że inny kod może dotykać tych tabel i nie ufam ludziom, aby zaktualizowali je poprawnie. :)
Xenph Yan
26

Jeśli zdecydujesz się, aby MySQL zajmował się aktualizacją znaczników czasu, możesz ustawić wyzwalacz, aby zaktualizować pole przy wstawianiu.

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

Źródła MySQL: http://dev.mysql.com/doc/refman/5.0/en/triggers.html

webkraller
źródło
Fajnie, tego właśnie szukałem. Nie chcę polegać na osobach dodanych TERAZ () do zapytania!
Bot,
właściwie byłoby lepiej zrobić po wstawieniu i ustawić NEW. <timestamp_field> = new. <timestamp_field, które faktycznie musi mieć domyślny current_timestamp> w ten sposób oba pola są spójne
KacieHouser
@KacieHouser Aktualizacja nowego wiersza nie jest dozwolona po wyzwoleniu
Thomas
23

W ten sposób możesz mieć automatyczne i elastyczne pola createDate / lastModified za pomocą wyzwalaczy:

Najpierw zdefiniuj je w ten sposób:

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

Następnie dodaj te wyzwalacze:

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);
  • Jeśli wstawisz bez określania createDate lub lastModified, będą one równe i ustawione na bieżący znacznik czasu.
  • Jeśli zaktualizujesz je bez określenia createDate lub lastModified, lastModified zostanie ustawiony na bieżący znacznik czasu.

Ale oto fajna część:

  • Jeśli wstawisz , możesz określić createDate starszą niż bieżący znacznik czasu , dzięki czemu importowanie ze starszych czasów będzie działać dobrze (lastModified będzie równe createDate).
  • Jeśli aktualizujesz , możesz określić lastModified starszą niż poprzednia wartość ('0000-00-00 00:00:00' działa dobrze), umożliwiając aktualizację wpisu, jeśli robisz kosmetyczne zmiany (poprawianie literówki w komentarzu ) i chcesz zachować starą datę LastModified . Nie spowoduje to zmiany daty ostatniej modyfikacji.
obcy
źródło
21

Od MySQL 5.6 jest to łatwe ... spróbuj:

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
źródło
Prawdopodobnie świetne rozwiązanie To, czego szukam, problem z czasem tworzenia i aktualizacji bez wyzwalacza
matinict
Nie mogę uwierzyć, że rozwiązanie tego małego problemu zajęło tyle czasu. Nawet nie zdawałem sobie sprawy, że to naprawili i jest 2018 ... Dzięki.
hsanders
4

Wydawało się, że ten problem został rozwiązany w MySQL 5.6. Zauważyłem to do MySQL 5.5; oto przykładowy kod:

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;

Uruchomienie tego na MySQL 5.5 daje:

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

Uruchomienie tego na MySQL 5.6

0 row(s) affected   0.093 sec
Kingz
źródło
2

W przypadku mysql 5.7.21 używam następujących i działa dobrze:

CREATE TABLE Posts( modified_atsygnatura czasowa NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, created_atsygnatura czasowa NOT NULL DEFAULT CURRENT_TIMESTAMP)

Ioannis Chrysochos
źródło
1

Myślę, że jest to lepsze zapytanie dla stamp_created i 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() 
); 

bo kiedy tworzony rekord, stamp_createdpowinien być wypełniony now()i stamp_updatedpowinien być wypełniony przez'0000-00-00 00:00:00'

Anonim
źródło