LoginSignup
17
1

More than 5 years have passed since last update.

ENUM値のラベル名変更

Last updated at Posted at 2017-12-02

はじめに

にゃーん
この記事は、PostgreSQL 10全部ぬこ Advent Calendar 2017 の3日目のエントリです。

ENUMとは

ENUMとはENUMERATION(列挙)型は値域を規定する一種のデータタイプ。例えば、神奈川県の市という内容のkanagawa_cityという列挙型を定義してみる。

enum=#  CREATE TYPE kanagawa_city AS ENUM ('横浜','川崎','相模原','町田','横須賀');
CREATE TYPE
enum=#

そしてこのkanagawa_city型の列を含むテーブルを作成する。

enum=# CREATE TABLE test (id int, city kanagawa_city);
CREATE TABLE
enum=# \d test
                   Table "public.test"
 Column |     Type      | Collation | Nullable | Default
--------+---------------+-----------+----------+---------
 id     | integer       |           |          |
 city   | kanagawa_city |           |          |

このtestテーブルのcity列にENUMで列挙した値域以外を設定すると怒られる。
(横浜や町田はkanagawa_cityの値域なので挿入や更新ができるが、立川や八王子はkanagawa_cityの値域ではないのでエラーになる)

enum=# INSERT INTO test VALUES (1, '横浜');
INSERT 0 1
enum=# INSERT INTO test VALUES (2, '立川');
ERROR:  invalid input value for enum kanagawa_city: "立川"
LINE 1: INSERT INTO test VALUES (2, '立川');
                                    ^
enum=# UPDATE test SET city = '町田';
UPDATE 1
enum=# UPDATE test SET city = '八王子';
ERROR:  invalid input value for enum kanagawa_city: "八王子"
LINE 1: UPDATE test SET city = '八王子';

データベース側で列値を規定するという機能だが、「SQLアンチパターン」の「31のフレーバー」の章でも挙げられているように、利用時にはちょいと検討が必要な機能でもある。

参考:PostgreSQLでの管理方法

PostgreSQLではENUMをpg_enumというシステムカタログで管理している。
例えば、さっきのkanagawa_cityという列挙型は以下のようにpg_enumに格納されている。

enum=# TABLE pg_enum;
 enumtypid | enumsortorder | enumlabel 
-----------+---------------+-----------
     16396 |             1 | 横浜
     16396 |             2 | 川崎
     16396 |             3 | 相模原
     16396 |             4 | 町田
     16396 |             5 | 横須賀
(5 rows)

enumsortorder に注意。ソートするときにはここの値でソートされ、enumlabel の値ではソートされない。

ENUM値の変更(PostgreSQL 9.6まで)

さて、CREATE TYPEで作成したENUMの内容を変更したい、というケースもあるかもしれない。

  • 列挙値を追加したい
  • 列挙値を削除したい
  • 列挙値のラベルを変更したい。

PostgreSQL 9.6までは列挙値の追加のみ対応していた。

enum=#  CREATE TYPE kanagawa_city AS ENUM ('横浜','川崎','相模原','横須賀');
CREATE TYPE

kanagawa_city型の値域として、町田を追加する。

enum=# ALTER TYPE kanagawa_city ADD VALUE '町田';
ALTER TYPE
enum=# TABLE pg_enum;
 enumtypid | enumsortorder | enumlabel 
-----------+---------------+-----------
     41868 |             1 | 横浜
     41868 |             2 | 川崎
     41868 |             3 | 相模原
     41868 |             4 | 横須賀
     41868 |             5 | 町田
(5 rows)

町田がkanagawa_cityに追加された。町田は神奈川。

なお、PostgreSQL 9.6までは、一度追加したラベルの内容を変更することはできない。
また、ラベルの削除もできない。これがENUMは変更に弱い、という問題に繋がると。
もしラベルを変更したければ、

  • この列挙型を使っている全テーブルをCOPY等でEXPORT、
  • この列挙型を使っている全テーブルをDROP
  • DROP TYPE ... CASCADEで列挙型を削除
  • ラベルを変更した列挙型をCREATE TYPEで再生成
  • この列挙型を使っていた全テーブルをCREATE TABLEで再生性
  • EXPORTしたデータを再ロード

という手順が必要になり、ちょっと実運用ではやってられんよなあと。

PostgreSQL 10での改善

PostgreSQL 10では、列挙型に追加ずみのラベルを変更可能になった。

現在のkanagawa_city型の列挙値はこんな感じ。

enum=# TABLE pg_enum;
 enumtypid | enumsortorder | enumlabel 
-----------+---------------+-----------
     41868 |             1 | 横浜
     41868 |             2 | 川崎
     41868 |             3 | 相模原
     41868 |             4 | 横須賀
     41868 |             5 | 町田
(5 rows)

で、この列挙型を使った列を定義して、データを格納する。

enum=# CREATE TABLE test (id int primary key, data kanagawa_city);
CREATE TABLE
enum=# INSERT INTO test VALUES (1, '横浜');
INSERT 0 1
enum=# TABLE test;
 id | data 
----+------
  1 | 横浜
(1 row)

ここで、「横浜」というラベルを「横濱」に変更する。

enum=# ALTER TYPE kanagawa_city RENAME VALUE '横浜' TO '横濱';
ALTER TYPE
enum=# TABLE test;
 id | data 
----+------
  1 | 横濱
(1 row)

ALTER TYPEの終了後にtestテーブルを参照すると、「横浜」は「横濱」に変更されている。

おわりに

町田は神奈川。

PostgreSQL 10ではENUMのラベル変更を大きなコストをかけずに実施できるようになった。
これでENUMの「変更に弱い」という問題点の一部は解決できそう。
残念ながらラベルの削除は未対応。つか、既に使用されているラベルの削除したらどうするんだ、みたいな仕様上の問題の整理から必要なんだろうなあ。

参考:該当するリリースノート

本エントリに関連するPostgreSQL 10リリースノートの記載です。

E.2.3.5. Data Types

  • Allow ENUM values to be renamed (Dagfinn Ilmari Mannsaker)
17
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
1