50

I'm trying to do this, but it seems like MySQL isn't allowing me. Is there a solution to this issue or am I expected to always include the function in my INSERT queries?

CREATE TABLE foo(
  created INT NOT NULL DEFAULT UNIX_TIMESTAMP()
)

I'm aware of the TIMESTAMP type that accepts a CURRENT_TIMESTAMP default, but my client insisted on using epoch time in the database.

Kit Sunde
  • 35,972
  • 25
  • 125
  • 179
  • Is it necessary to have NOT NULL and DEFAULT together? because with a specified default value, it will never be null. – T S Feb 12 '22 at 14:15
  • 2
    @TS It's a bit odd to respond to a question I asked over 10 years ago, but knowing *a lot* more about databases these days I can tell you that some databases (this is definitely true in PostgreSQL so should be for MySQL) will choose better optimization paths when it knows a column isn't nullable. If you set a default but allow a column to be nullable then it's acceptable to give it NULL values. Give your database as much information as you can so it can plan execution better. – Kit Sunde Feb 17 '22 at 08:02
  • @TS and how you know it will never be Null? by writing NOT NULL. – Mathias Osterhagen Jun 10 '22 at 14:12

6 Answers6

63

The way MySQL implements the TIMESTAMP data type, it is actually storing the epoch time in the database. So you could just use a TIMESTAMP column with a default of CURRENT_TIMESTAMP and apply the UNIX_TIMESTAMP() to it if you want to display it as an int:

CREATE TABLE foo(
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

insert into foo values (current_Date()),(now());

select unix_timestamp(created) from foo;
+-------------------------+
| unix_timestamp(created) |
+-------------------------+
|              1300248000 |
|              1300306959 |
+-------------------------+
2 rows in set (0.00 sec)

However, if you really want the datatype of the column to be INT, you can use R. Bemrose's suggestion and set it via trigger:

CREATE TABLE foo(
  created INT NULL
);

delimiter $$

create trigger tr_b_ins_foo before insert on foo for each row
begin
  if (new.created is null)
  then
    set new.created = unix_timestamp();
  end if;
end $$

delimiter ;


insert into foo values (unix_timestamp(current_Date())), (null);

select created from foo;
+------------+
| created    |
+------------+
| 1300248000 |
| 1300306995 |
+------------+
2 rows in set (0.00 sec)
Ike Walker
  • 64,401
  • 14
  • 110
  • 109
  • 1
    Is there a difference in performance, querying against TIMESTAMP vs. BIGINT? It is really stored physically as a string, or is it stored efficiently? Tnx. – JRun Mar 30 '14 at 06:12
  • 4
    Anyone choosing a data type should keep in mind that TIMESTAMP faces the same 2038 but as the INT. A reason for considering a UNIX_TIMESTAMP (Epoch) stored in UNSIGNED INT(11) is to push the overflow bug out to the next century (BIGINT even further). Chances are your code will need to be rewritten by then as new technology will emerge. 2038, however, will happen in many of our lifetimes. The databases we construct today could still be around as legacy systems at that point. @JRun - I expect TIMESTAMP in MySQL is treated as an INT on the back end given it has the same numeric limitations. – Zack Jannsen Feb 06 '16 at 11:26
  • 1
    I should clarify - my comment above is relative to MySQL 5.5. Who knows what future versions will do. Per the developer documentation: – Zack Jannsen Feb 06 '16 at 11:35
  • 1
    Here is a reference to my point. Note the range is mathematically the same as a "signed" INT. "Per the developer documentation: The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC." URL: https://dev.mysql.com/doc/refman/5.5/en/datetime.html – Zack Jannsen Feb 06 '16 at 11:41
20

From the documentation:

With one exception, the default value must be a constant; it cannot be a function or an expression. This means, for example, that you cannot set the default for a date column to be the value of a function such as NOW() or CURRENT_DATE. The exception is that you can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP column.

Joe Stefanelli
  • 132,803
  • 19
  • 237
  • 235
10

You can create triggers for this.

for insertion

  • query

CREATE TRIGGER {trigger_name} BEFORE INSERT ON {table_name} FOR EACH ROW SET new.{field_name} = UNIX_TIMESTAMP(NOW());

  • in this case

CREATE TRIGGER my_trigger_name_1 BEFORE INSERT ON foo FOR EACH ROW SET new.created = UNIX_TIMESTAMP(NOW());

for update

  • query

CREATE TRIGGER {trigger_name} BEFORE UPDATE ON {table_name} FOR EACH ROW SET new.{field_name} = UNIX_TIMESTAMP(NOW());

  • in this case

CREATE TRIGGER my_trigger_name_2 BEFORE UPDATE ON foo FOR EACH ROW SET new.created = UNIX_TIMESTAMP(NOW());

Note : I have no idea about the performance of MYSQL TRIGGER

Please go through these links

  1. Identify some of the drawback of implementing sql server triggers

  2. Using Triggers

Community
  • 1
  • 1
Arjun Raj
  • 984
  • 2
  • 12
  • 32
7

As ktretyak's answer, with MySQL v8+, you can do that using a parentheses.

CREATE TABLE foo(
  created INT NOT NULL DEFAULT (UNIX_TIMESTAMP())
)

After some inserts, you can see it works correctly.
(Tested on MySQL 8.0.26 + only text color patched)

tested case

Alternatively you can use an expression, too.
Check ktretyak's answer.

I write this answer instead of comment or edit answer(tried and rejected)
because of lack of REPUTATION and using a parentheses is good solution now.

SG H
  • 81
  • 1
  • 2
  • Is it necessary to have NOT NULL and DEFAULT together? because with a specified default value, it will never be null. – T S Feb 12 '22 at 14:14
  • @T S Sorry for late. Uh... actually no. You can insert null if there no `NOT NULL` exist and run `INSERT INTO foo(created) VALUES(null);` will be allowed. I'm not sure but as I know there are performance issue to make a index with or without `NOT NULL` column, too. – SG H Feb 20 '22 at 21:16
  • holy W this should be the top answer, TYSM – DexieTheSheep May 07 '23 at 18:53
5

Well, if MySQL won't let you do it directly, you can always do it using a BEFORE INSERT... FOR EACH ROW trigger.

Powerlord
  • 87,612
  • 17
  • 125
  • 175
1

Now, with MySQL v8+, you can to do this just in parentheses:

CREATE TABLE t1 (
  -- literal defaults
  i INT         DEFAULT 0,
  c VARCHAR(10) DEFAULT '',
  -- expression defaults
  f FLOAT       DEFAULT (RAND() * RAND()),
  b BINARY(16)  DEFAULT (UUID_TO_BIN(UUID())),
  d DATE        DEFAULT (CURRENT_DATE + INTERVAL 1 YEAR),
  p POINT       DEFAULT (Point(0,0)),
  j JSON        DEFAULT (JSON_ARRAY())
);

See docs

ktretyak
  • 27,251
  • 11
  • 40
  • 63
  • And? Have you tested for `UNIX_TIMESTAMP()` instead of citing what doesn't relate to the original Q? Please edit your A accordingly. – AmigoJack Sep 21 '21 at 21:59