104

I want to do something like this:


create table app_users
(
    app_user_id smallint(6) not null auto_increment primary key,
    api_key     char(36) not null default uuid()
);

However this results in a error, is there a way to call a function for a default value in mysql?

thanks.

mmattax
  • 27,172
  • 41
  • 116
  • 149

9 Answers9

145

No, you can't.

However, you could easily create a trigger to do this, such as:

CREATE TRIGGER before_insert_app_users
  BEFORE INSERT ON app_users 
  FOR EACH ROW
  SET new.api_key = uuid();
Harrison Fisk
  • 7,074
  • 3
  • 24
  • 14
  • 2
    @See http://stackoverflow.com/questions/6280789/generate-guid-in-mysql-for-existing-data for populating UUIDs in existing rows. – Sam Barnum Oct 22 '13 at 17:09
  • 3
    This isn't quite the same as having a DEFAULT value. How would one change this answer to only set the key if the value was NULL? – ToolmakerSteve Apr 01 '15 at 17:38
  • I modified it with md5(uuid()) and worked fine. Was my aproach ok? – Rodolfo Velasco May 13 '15 at 16:46
  • 1
    Sad that MySQL has been around for as long as it has but you can't implement a default UUID for a column without a trigger. The reason for this is embedded in the 1980's technology that apparently still exists in the MySQL base code (search for"we can't have nice things"): https://www.percona.com/blog/2013/04/08/understanding-the-maximum-number-of-columns-in-a-mysql-table/ – RyanNerd Dec 11 '17 at 22:55
  • 1
    @RodolVelasco the uuid function is built to be highly collision resistant. Much more so than md5. As soon as you md5 the uuid you have a higher risk of id collision – Cruncher Jan 24 '18 at 21:55
  • 6
    `SET new.api_key = COALESCE(new.api_key, uuid())` to preserve existing values. – Ryan Apr 20 '18 at 05:01
  • 2
    This is no true. It is possible to have a function set to a default value... at least nowadays. This answer has to be updated in order to keep its status of "Right Answer" – asiby Nov 27 '19 at 15:59
55

As of mysql v8.0.13 it is possible to use an expression as a default value for a field:

The default value specified in a DEFAULT clause can be a literal constant or an expression. With one exception, enclose expression default values within parentheses to distinguish them from literal constant default values.

CREATE TABLE t1 (
  uuid_field     VARCHAR(32) DEFAULT (uuid()),
  binary_uuid    BINARY(16)  DEFAULT (UUID_TO_BIN(UUID()))
);
Shadow
  • 33,525
  • 10
  • 51
  • 64
  • 7
    Note that as per the already linked docs in answer: _... stored functions, and user-defined functions are not permitted_. I.e., the only functions that can be used as default expressions are built-in functions. – asherbret Mar 29 '20 at 21:19
  • 5
    a minor detail: `varchar(32)` should be `varchar(36)` – Jared Beck Oct 27 '20 at 19:08
  • @JaredBeck I think 32 is fine if you're not storing the dashes. – Alvin Thompson Nov 02 '20 at 19:13
  • Note that if you are using MySQL Workbench, and decide to alter this table column at a later time with the given default previously set, Workbench removes the outer parenthesis creating an error. – Rich76 Jul 23 '22 at 11:45
  • For those using MariaDB a direct [UUID type](https://mariadb.com/kb/en/uuid-data-type/) without the `UUID_TO_BIN` (only the [UUID() function](https://mariadb.com/kb/en/uuid/) like [this answer](https://dba.stackexchange.com/a/326456/158002). – danblack Apr 26 '23 at 22:16
24

As already stated you can't.

If you want to simulate this behavior you can use a trigger in this way:

CREATE TRIGGER before_insert_app_users
BEFORE INSERT ON app_users
FOR EACH ROW
  IF new.uuid IS NULL
  THEN
    SET new.uuid = uuid();
  END IF;

You still have to update previously existing rows, like this:

UPDATE app_users SET uuid = (SELECT uuid());
dolmen
  • 8,126
  • 5
  • 40
  • 42
Pamput
  • 2,634
  • 2
  • 16
  • 16
9

Unfortunately no, MySQL 5 requires constants for the default. The issue was discussed in much more detail in the link below. But the only answer is to allow null and add a table trigger.

MySQL only recently accepted UUID as part of their DB package, and it's not as feature rich as we'd like.

http://www.phpbuilder.com/board/showthread.php?t=10349169

TravisO
  • 9,406
  • 4
  • 36
  • 44
  • 1
    I should add that allowing NULL and relying on triggers may work, but most developers would consider it a very hack-y solution. I wouldn't personally recommend it but to each their own. – TravisO Sep 13 '13 at 18:58
7

I believe you can't:

the default value must be a constant; it cannot be a function or an expression

Thibaut Barrère
  • 8,845
  • 2
  • 22
  • 27
  • Not true, you can use getdate() – amr osama Jan 12 '12 at 09:18
  • 6
    @amrosama - No, you cannot. `getdate()` is not even a MySQL function. The link in the answer explains the only exception: *«you can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP column»*. – Álvaro González Feb 28 '13 at 09:46
  • 2
    CURRENT_TIMESTAMP is the only "function" that can be used as a default value. Everything else must be a constant (unfortunately). – Troy Morehouse Feb 15 '15 at 09:33
  • 3
    Technically I'd say AUTO_INCREMENT can also be seen as a "function" to dynamically set a default value. – Rikaelus Jun 02 '17 at 03:55
3

In MariaDB starting from version 10.2.1 you can. See its documentation.

CREATE TABLE test ( uuid BINARY(16) PRIMARY KEY DEFAULT unhex(replace(uuid(),'-','')) );
INSERT INTO test () VALUES ();
SELECT * FROM test;
Madhur Bhaiya
  • 28,155
  • 10
  • 49
  • 57
ibotty
  • 707
  • 4
  • 10
2

Note that MySQL's UUID() returns CHAR(36), and storing UUIDs as text (as shown in the other answers) is obviously inefficient. Instead, the column should be BINARY(16), and you can use UUID_TO_BIN() when inserting data and BIN_TO_UUID() when reading it back.

CREATE TABLE app_users
(
    app_user_id SMALLINT(6) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    api_key     BINARY(16)
);

CREATE TRIGGER before_insert_app_users
BEFORE INSERT ON app_users
FOR EACH ROW
  IF new.api_key IS NULL
  THEN
    SET new.api_key = UUID_TO_BIN(UUID());
  END IF;

Note that since MySQL doesn't really know this is a UUID, it can be difficult to troubleshoot problems with it stored as binary. This article explains how to create a generated column that will convert the UUID to text as needed without taking up any space or worrying about keeping separate binary and text versions in sync: https://mysqlserverteam.com/storing-uuid-values-in-mysql-tables/

StephenS
  • 1,813
  • 13
  • 19
2

Harrison Fisk's answer was great when it was written, but now it's outdated.

Nowadays you can use an expression as a DEFAULT value. This is supported since MySQL 8.0 and MariaDB 10.2. Note that, if you're going to use non-deterministic functions like NOW() or USER(), you should not use binlog_format=statement.

  • The reference manual for 5.7 states otherwise. -> "the default value specified in a DEFAULT clause must be a literal constant; it cannot be a function or an expression" https://dev.mysql.com/doc/refman/5.7/en/data-type-defaults.html. – Devon Sams Nov 11 '21 at 14:04
  • 1
    You are right, I fixed my answer. – Federico Razzoli Nov 11 '21 at 15:06
0

I'm not sure if the above answers are for an older version, but I saw somewhere that you can do this using the unhex() function. I tried it and it works. (maria db version 10.2)

You can do

.... column_name binary(16) not null default unhex(replace(uuid(),'-',''))   

and it works. To see the uuid just do hex(column_name).

ravindu1024
  • 1,496
  • 1
  • 13
  • 30