Enumeration in PostgreSQL
By: Anjuman Bhattacharyya
Tags: Database, Postgresql, Sql, Enumeration, Enum

This article deals with the following

Introduction

Create ENUM in PostgreSQL

View ENUM values

Comparison between different ENUMs’ values

Add a value in ENUM

Update ENUM value

Remove ENUM value

Drop ENUM

Conclusion

Introduction

Enumeration deals with a certain set of static values. Enumeration or ENUM can be used in PostgreSQL if one needs to make sure that some columns in database need to have specific set of values that never or seldom change. This article throws some light on how to work with ENUM in PostgreSQL with example.

Create ENUM in PostgreSQL

I consider one sales data table where sales related records are stored. Sales regions are to be specified as ‘Central’, ‘ North’, ‘South’, ‘East’, ‘West’ in that table . No other value is to be inserted. Hence I create an ENUM named sale_region_type in postgreSQL to specify these values. ENUM is created as follows

create type sale_region_type as ENUM

(

‘Central’,

‘North’,

‘South’,

‘East’,

‘West’

);

One important point to remember is that ENUM values are case sensitive. So ‘Central’ and ‘central’ is different for enumerated types.

Next, I create a table to store sales records. I like to keep product number, date of sale, quantity of items sold, name of the item and lastly region of sale.

CREATE TABLE sales_data

( id serial,

product_no integer,

sale_date date,

sale_quantity integer,

item_name text,

sale_region sale_region_type,

CONSTRAINT sales_data_pk PRIMARY KEY(id)

);

Data type of column sale_region is of sale_region_type which is an ENUM created earlier.

I can insert some sample records where region of sale belongs to any of the values defined in sale_region_type.

INSERT INTO sales_data(product_no,sale_date,sale_quantity,item_name,sale_region) values

(5001,’2021–05–03',10,’Soap’,’Central’),

(2002,’2021–05–02',25,’Chocolates’,’East’),

(2002,’2021–05–02',30,’Sanitizer’,’West’);

All 3 rows are inserted successfully. A SELECT query fetches data as follows

Result of SELECT all records from table sales_data

Now, if I try to insert a record with a sales region name which is absent in ENUM values it shows an error and insertion operation is failed. For example

INSERT INTO sales_data (product_no,sale_date,sale_quantity,item_name,sale_region)

VALUES (2003,’2021–05–02',500,’Mask’,’North West’)

As a result an error is shown as

Error in inserting non ENUM values

View ENUM Values

Viewing ENUM values can be done easily by executing a query joining two system catalogue tables namely pg_type and pg_enum ( Click here to find more on these two tables). Details of ENUMs are stored in system catalogue table pg_enum.

SELECT t.typname, e.enumlabel FROM pg_type t INNER JOIN pg_enum e

on t.oid = e.enumtypid;

Output

Viewing result for ENUM

Comparison between different ENUMs’ values

Values of two ENUMs can not be compared in PostgreSQL directly. Let’s consider one scenario. I create another ENUM with same values as sale_region_type with different order of the values.

CREATE TYPE ware_house_region_type AS ENUM

(

‘Central’,

‘North’,

‘East’,

‘West’,

‘South’

);

Now, I create another table ware_house to store warehouse id and warehouse region. Here warehouse region is of ENUM type. Then I insert sample values into that table.

CREATE TABLE ware_house (

ware_house_id integer,

ware_house_region ware_house_region_type,

CONSTRAINT ware_house_pk primary key (ware_house_id));

INSERT INTO ware_house VALUES (91001,’Central’),

(91002,’South’);

I try to join two tables sales_data and ware_house on the basis of sale_region and ware_house_region with the following query

SELECT product_no,item_name,sale_region,ware_house_id,ware_house_region

FROM sales_data INNER JOIN ware_house ON sale_region=ware_house_region;

It shows an error with statement

ERROR: operator does not exist: sale_region_type = ware_house_region_type

Hence, it is understood that direct comparison is not possible between values of two ENUMs. If we need to do this then we have to execute query with typecasting.

SELECT product_no,item_name,sale_region,ware_house_id,ware_house_region

FROM sales_data INNER JOIN ware_house ON sale_region::text=ware_house_region::text;

The above query shows a proper output.

Output after comparison

Add a value in ENUM

A new value can be added to ENUM in any place. ENUM maintains a sort order in which value is inserted and kept. One can add values in sale_region_type by executing the following

ALTER TYPE sale_region_type ADD VALUE ‘North East’ BEFORE ‘West’;

ALTER TYPE sale_region_type ADD VALUE ‘North West’ BEFORE ‘North East’;

Let’s analyze sorted order of ENUM while adding above two values. After addition, the ENUM values with their respective sort order can be viewed as below

ENUM values with sort order

As per the above query ‘North East’ has been added first. So, sort order of ‘North East’ is assigned as 4.5 . Sort order 4 and 5 are already assigned with values ‘East’ and ‘West’ respectively. Then ‘North West’ is added in the list before ‘North East’. As ‘North East’ is already assigned with sort order 4.5 , so ‘North West’ is assigned with sort order 4.25.

Update ENUM value

PostgreSQL version 10+ is capable of updating an existing value in ENUM. I want to update value ‘Central’ to ‘Centre’. It can be done easily by executing the below command.

ALTER TYPE sale_region_type RENAME VALUE ‘Central’ To ‘Centre’;

Updated ENUM

Updated ENUM can be viewed using the same query used for viewing ENUM values.

Updated record in sale_region column

Now, if I fetch the records of table sales_data then I can find all records in sale_region column with value as ‘Central’ have been updated to ‘Centre’.

Remove ENUM value

As per latest PostgreSQL 13 documentation, existing values can not be removed from enumeration. Even sort order of values can not be changed. Removing of ENUM requires dropping of enumeration type and recreating a new one with new values.

Drop ENUM

Dropping of ENUM can be done by deleting the type used for creating ENUM. As a result the sale_region cloumn in this very example is also dropped from sales_data table. The next query drops sale_region_type.

DROP TYPE sale_region_type CASCADE;

If CASCADE is not mentioned then PostgreSQL throws an error.

ERROR: cannot drop type sale_region_type because other objects depend on it

However, if sale_region_type is not mapped with any column of any table then I can execute the easier one to drop the same.

DROP TYPE sale_region_type;

Conclusion