MySQL и автоматическое обновление таймстампов

· На чтение уйдёт 2 минут · (409 слов)

Несмотря на то, что сейчас такие вещи, как последнее обновление записи, модно делать с помощью ORM, — в том случае, если вы можете быть платформенно-зависимы (всю жизнь с MySQL, или готовы переписать триггеры в случае перехода на другую БД вроде PostgreSQL), возможно, есть смысл воспользоваться триггерами.

MySQL позволяет сделать один автообновляемый таймстамп, например таким образом:

CREATE TABLE games (name VARCHAR(64), created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);

Либо на обновление:

CREATE TABLE games (name VARCHAR(64), updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);

А вот совместить их нельзя. Зато можно сделать так:

CREATE TABLE games (name VARCHAR(64), created TIMESTAMP NOT NULL DEFAULT ‘0000-00-00’, updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);

Благодаря полубаге/полуфиче, можно теперь сделать так:

INSERT INTO games(name, created) VALUES (‘123’, NULL);

Таким образом в таблицу будет записано текущее значение. Подробности — в документации MySQL.

Вместе с тем, мне больше по душе другой вариант, реализация через триггеры. Поле created — используем как приведено раньше, а вот полю updated устанавливаем дефолтное значение в 0 или ‘0000-00-00’.

CREATE TABLE games (name VARCHAR(64), 
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
updated TIMESTAMP NOT NULL DEFAULT 0);
DELIMITER ;;
DROP TRIGGER IF EXISTS games_update;;
DROP TRIGGER IF EXISTS games_create;;
CREATE TRIGGER games_update BEFORE UPDATE ON games FOR EACH ROW 
BEGIN 
  SET NEW.updated = NOW(); 
END 
;;
CREATE TRIGGER games_create BEFORE INSERT ON games FOR EACH ROW 
BEGIN 
  IF NEW.updated = 0 THEN
    SET NEW.updated = CURRENT_TIMESTAMP;
  END IF;
END 
;;
DELIMITER ;

Такой вариант я использовал, такой вариант работает. Если массово добавляются записи с другим created, то этот вариант не прокатит. И тогда я переписал его полностью на триггеры:


CREATE TABLE games (name VARCHAR(64), 
created TIMESTAMP NOT NULL DEFAULT 0, 
updated TIMESTAMP NOT NULL DEFAULT 0);
DELIMITER ;;
DROP TRIGGER IF EXISTS games_update;;
DROP TRIGGER IF EXISTS games_create;;
CREATE TRIGGER games_update BEFORE UPDATE ON games FOR EACH ROW 
BEGIN 
  SET NEW.updated = NOW(); 
END 
;;
CREATE TRIGGER games_create BEFORE INSERT ON games FOR EACH ROW 
BEGIN
  IF NEW.created = 0 THEN
    SET NEW.created = CURRENT_TIMESTAMP;
  END IF;
  IF NEW.updated = 0 THEN
    SET NEW.updated = NEW.created;
  END IF;
END 
;;
DELIMITER ;

Теперь NEW.created задаётся раньше, чем NEW.updated, и его можно использовать.

Например:

INSERT INTO games(name) VALUES ('123');
INSERT INTO games(name, created) VALUES ('222', '2011-01-01');
SELECT * FROM games;
+------+---------------------+---------------------+
| name | created             | updated             |
+------+---------------------+---------------------+
| 123  | 2011-09-20 23:43:11 | 2011-09-20 23:43:11 |
| 222  | 2011-01-01 00:00:00 | 2011-01-01 00:00:00 |
+------+---------------------+---------------------+

Всё работает, как задумано.

Полезное