Jul 10

Presja czasu czyli złośliwości timestampa w MySQL

Tag: MySQLmyszaq @ 09:07

Niedawno natrafiłem na dosyć osobliwy problem podczas definiowania kolumn w jednej z tabeli. Okazuje się, że wszystkim dobrze znany (mam taką nadzieję:)) typ TIMESTAMP rządzi się swoimi własnymi prawami i potrafi w pewnych sytuacjach namieszać dość konkretnie. O co chodzi? Wyobraźmy sobie, że chcemy stworzyć w tabeli ‘czasy’ 2 pola:

updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
edited_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

Niby wszystko jest w porządku, jednak pojawia się komunikat:

Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause.

Niestety twórcy MySQL, chociaż opisali taką sytuację, nie raczyli napisać dlaczego tak się dzieje. Próba zastąpienia CURRENT_TIMESTAMP przez NOW() tudzież LOCALTIME() też nic nie daje, jako że są to wartości równoznaczne tej pierwszej. Warto przy tym wspomnieć, że typ TIMESTAMP daje dość duże możliwości, jeżeli chcemy dać polu wartość domyślną i/lub automatycznie uaktualniać przy użyciu nieszczęsnego CURRENT_TIMESTAMP. Możemy użyć go np. do czegoś takiego:
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
lub
updated_at TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP.

Wspaniale, tylko że dotyczy to pierwszej kolumny. Próba użycia CURRENT_TIMESTAMP w kolejnej kolumnie zakończy się powyższym błędem.
Co więc zrobić, gdy chcemy koniecznie mieć możliwość zdefiniowania defaultowej wartości dla obu takich pól czasowych (o aktualizacji nie wspominając)? Możemy zdefiniować pola następująco:

updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
edited_at TIMESTAMP NOT NULL DEFAULT '2008-01-01 00:00:00'

Zamiast jakiejś daty w polu DEFAULT możemy umieścić 0 lub nawet NULL, ale w tym ostatnim przypadku trzeba również pamiętać o zadeklarowaniu pola jako NULL. Takie pośrednie rozwiązania zapewne jednak nas nie satysfakcjonują - data w drugim polu stanie się szybko przestarzała a wartość zerowa do niczego się raczej nie przyda. Niestety tylko takie wyjście proponuje
manual.
Najlepiej jest zatem zapewnić samemu wstawianie odpowiednich wartości. I tu można się bardzo zdziwić - nie trzeba bowiem pisać
INSERT INTO czasy VALUES (NOW(), CURRENT_TIMESTAMP);

Jak to? Definiujemy tabelę ‘czasy’ w ten sposób:
create table czasy (
created_at timestamp NOT NULL default '0000-00-00 00:00:00',
updated_at timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
);

Następnie wykonujemy zapytanie
INSERT INTO czasy(created_at,updated_at) VALUES (NULL,NULL);

Jaki będzie efekt? Obie kolumny będą zawierać tą samą wartość - CURRENT_TIMESTAMP :) Dzieje się tak dlatego, że przypisanie kolumnie zdefiniowanej jako NOT NULL wartości NULL zamieni ją na aktualny czas.
A powiadają, że szczęśliwi czasu nie liczą…

One Response to “Presja czasu czyli złośliwości timestampa w MySQL”

  1. buka says:

    Heh :) Niezły hack, żeby tylko nie załatali tego ‘buga’ w kolejnej wersji zanim pozwolą na więcej current_timestampów… A wyjście dużo prostsze niż zakładanie triggera podmieniającego inserta na current_timestamp.

Leave a Reply