Skip to content

ENUM

ENUM provides a way to provide an attribute for a row. Attributes with a small number of non-numeric options work best. Examples:

reply ENUM('yes', 'no')
gender ENUM('male', 'female', 'other', 'decline-to-state')

The values are strings:

INSERT ... VALUES ('yes', 'female')
SELECT ... --> yes female

Let’s say we have

type ENUM('fish','mammal','bird')

An alternative is

type VARCHAR(20) COMENT "fish, bird, etc"

This is quite open-ended in that new types are trivially added.

Comparison, and whether better or worse than ENUM:

  • (same) INSERT: simply provide the string
  • (worse?) On INSERT a typo will go unnoticed
  • (same) SELECT: the actual string is returned
  • (worse) A lot more space is consumed
ALTER TABLE tbl MODIFY COLUMN type ENUM('fish','mammal','bird','insect');

Notes

  • As with all cases of MODIFY COLUMN, you must include NOT NULL, and any other qualifiers that originally existed, else they will be lost.
  • If you add to the end of the list and the list is under 256 items, the ALTER is done by merely changing the schema. That is there will not be a lengthy table copy. (Old versions of MySQL did not have this optimization.)

Examples of what happens when NULL and ‘bad-value’ are stored into nullable and not nullable columns. Also shows usage of casting to numeric via +0.

CREATE TABLE enum (
e ENUM('yes', 'no') NOT NULL,
enull ENUM('x', 'y', 'z') NULL
);
INSERT INTO enum (e, enull)
VALUES
('yes', 'x'),
('no', 'y'),
(NULL, NULL),
('bad-value', 'bad-value');
Query OK, 4 rows affected, 3 warnings (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 3
mysql>SHOW WARNINGS;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1048 | Column 'e' cannot be null |
| Warning | 1265 | Data truncated for column 'e' at row 4 |
| Warning | 1265 | Data truncated for column 'enull' at row 4 |
+---------+------+--------------------------------------------+
3 rows in set (0.00 sec)

What is in the table after those inserts. This uses “+0” to cast to numeric see what is stored.

mysql>SELECT e, e+0 FROM enum;
+-----+-----+
| e | e+0 |
+-----+-----+
| yes | 1 |
| no | 2 |
| | 0 | -- NULL
| | 0 | -- 'bad-value'
+-----+-----+
4 rows in set (0.00 sec)
mysql>SELECT enull, enull+0 FROM enum;
+-------+---------+
| enull | enull+0 |
+-------+---------+
| x | 1 |
| y | 2 |
| NULL | NULL |
| | 0 | -- 'bad-value'
+-------+---------+
4 rows in set (0.00 sec)

Let’s say we have

type ENUM('fish','mammal','bird')

An alternative is

type TINYINT UNSIGNED

plus

CREATE TABLE AnimalTypes (
type TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(20) NOT NULL COMMENT "('fish','mammal','bird')",
PRIMARY KEY(type),
INDEX(name)
) ENGINE=InnoDB

which is very much like a many-to-many table.

Comparison, and whether better or worse than ENUM:

  • (worse) INSERT: need to lookup the type
  • (worse) SELECT: need to JOIN to get the string (ENUM gives you the string with no effort)
  • (better) Adding new types: Simply insert into this table. With ENUM, you need to do an ALTER TABLE.
  • (same) Either technique (for up to 255 values) takes only 1 byte.
  • (mixed) There’s also an issue of data integrity: TINYINT will admit invalid values; whereas ENUM sets them to a special empty-string value (unless strict SQL mode is enabled, in which case they are rejected). Better data integrity can be achieved with TINYINT by making it a foreign key into a lookup table: which, with appropriate queries/joins, but there is still the small cost of reaching into the other table. (FOREIGN KEYs are not free.)