Whether to use or not to use: DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Tested on 5.5.34

I looked at this in detail (I'm interested for an upcoming project, and it's been a while since I've "got my hands dirty"...)

It appears that datetimes are hard to set defaults for - see here. From what I've read, things have got better in 5.6 - I'll see if I have time to test tomorrow.

I think that your best solution is as below. The answer to your question about the best way to CREATE your TABLE is shown (IMHO). It involves inversing the ON UPDATE clause, putting it (logically enough) with the updated_at field.

mysql&gt; CREATE TABLE mt8
    -&gt; (
    -&gt;   billy int,
    -&gt;   created_at timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Created At',
    -&gt;   updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Created At'
    -&gt; );
Query OK, 0 rows affected (0.16 sec)

mysql&gt; 
mysql&gt; 
mysql&gt; DELIMITER $$
mysql&gt; create trigger my_ins8_trigger
    -&gt; before insert on mt8
    -&gt; for each row
    -&gt; begin
    -&gt;   set new.created_at = current_timestamp;
    -&gt; end $$
Query OK, 0 rows affected (0.08 sec)

mysql&gt; DELIMITER ;
mysql&gt; 
mysql&gt; 
mysql&gt; insert into mt8 values(123, null, null);
Query OK, 1 row affected (0.08 sec)

mysql&gt; insert into mt8 values(454, null, null);
Query OK, 1 row affected (0.05 sec)

mysql&gt; insert into mt8 values(1676, null, null);
Query OK, 1 row affected (0.06 sec)

mysql&gt; insert into mt8 (billy) values(34334);
Query OK, 1 row affected (0.07 sec)

mysql&gt; insert into mt8 (billy) values(3506);
Query OK, 1 row affected (0.05 sec)

mysql&gt; insert into mt8 (billy) values(3435454);
Query OK, 1 row affected (0.05 sec)

mysql&gt; select * from mt8;
+---------+---------------------+---------------------+
| billy   | created_at          | updated_at          |
+---------+---------------------+---------------------+
|     123 | 2014-06-12 01:53:40 | 2014-06-12 01:53:40 |
|     454 | 2014-06-12 01:53:40 | 2014-06-12 01:53:40 |
|    1676 | 2014-06-12 01:53:40 | 2014-06-12 01:53:40 |
|   34334 | 2014-06-12 01:53:56 | 2014-06-12 01:53:56 |
|    3506 | 2014-06-12 01:53:56 | 2014-06-12 01:53:56 |
| 3435454 | 2014-06-12 01:53:56 | 2014-06-12 01:53:56 |
+---------+---------------------+---------------------+
6 rows in set (0.00 sec)

mysql&gt; update mt8 set billy = 455 where billy = 454;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql&gt; select * from mt8;
+---------+---------------------+---------------------+
| billy   | created_at          | updated_at          |
+---------+---------------------+---------------------+
|     123 | 2014-06-12 01:53:40 | 2014-06-12 01:53:40 |
|     455 | 2014-06-12 01:53:40 | 2014-06-12 01:54:26 |
|    1676 | 2014-06-12 01:53:40 | 2014-06-12 01:53:40 |
|   34334 | 2014-06-12 01:53:56 | 2014-06-12 01:53:56 |
|    3506 | 2014-06-12 01:53:56 | 2014-06-12 01:53:56 |
| 3435454 | 2014-06-12 01:53:56 | 2014-06-12 01:53:56 |
+---------+---------------------+---------------------+
6 rows in set (0.00 sec)
mysql&gt;


For the row with billy = 455, the update timestamp has changed, but not the created one - exactly as you'd want!