postgresの列挙型の値を削除する方法は?


109

postgresqlで作成した列挙型の値を削除するにはどうすればよいですか?

create type admin_level1 as enum('classifier', 'moderator', 'god');

たとえばmoderator、リストから削除したいです。

ドキュメントに何も見つからないようです。

Postgresql 9.3.4を使用しています。


4
drop type admin_level1
bereal

1
親指のルール:すべてのためにcreate xxxそこにあるdrop xxx
a_horse_with_no_nameは

IMO選択した回答を別の回答に変更する必要があります。
ローマポドリノフ

回答:


180

他のタイプと同様に列挙型を削除(ドロップ)しDROP TYPEます。

DROP TYPE admin_level1;

列挙型から個々の値削除する方法について実際に尋ねている可能性はありますか?その場合はできません。サポートされていません

enumタイプは主に静的な値のセットを対象としていますが、既存の列挙型に新しい値を追加したり、値の名前を変更したりすることもできます(を参照ALTER TYPE)。列挙型から既存の値を削除したり、列挙型を削除して再作成したりしない限り、そのような値の並べ替え順序を変更することはできません。

値なしで新しいタイプを作成し、古いタイプの既存の使用をすべて新しいタイプを使用するように変換してから、古いタイプをドロップする必要があります。

例えば

CREATE TYPE admin_level1 AS ENUM ('classifier', 'moderator');

CREATE TABLE blah (
    user_id integer primary key,
    power admin_level1 not null
);

INSERT INTO blah(user_id, power) VALUES (1, 'moderator'), (10, 'classifier');

ALTER TYPE admin_level1 ADD VALUE 'god';

INSERT INTO blah(user_id, power) VALUES (42, 'god');

-- .... oops, maybe that was a bad idea

CREATE TYPE admin_level1_new AS ENUM ('classifier', 'moderator');

-- Remove values that won't be compatible with new definition
-- You don't have to delete, you might update instead
DELETE FROM blah WHERE power = 'god';

-- Convert to new type, casting via text representation
ALTER TABLE blah 
  ALTER COLUMN power TYPE admin_level1_new 
    USING (power::text::admin_level1_new);

-- and swap the types
DROP TYPE admin_level1;

ALTER TYPE admin_level1_new RENAME TO admin_level1;

1
これは素晴らしいです!これにより、Alembicの移行問題を解決することができました。次の理由により、新しい列挙型を追加できませんでした(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan

disable_ddl_transactionを追加してください!移行ファイルの先頭に。
シェル、2018

何とかDELETE WHERE power = 'god'; 私の場合は機能しません
ankit '

1
TBHこの回答が選ばれた理由はわかりません。この答えは正しくありません!指定したラベルを使用して、pg_enumから値を削除できます。
ローマポドリノフ

2
@RomanPoelinov直接カタログ操作はあなた自身の責任でお願いします。postgresがenum値のネイティブな削除をサポートしていない理由があります。サポートされていない、安全でないカタログハックと比較して、これはどのように「正しくない」のでしょうか。
クレイグリンガー

41

ここに非常によく書かれています:

http://blog.yo1.dog/updating-enum-values-in-postgresql-the-safe-and-easy-way/

既存のタイプの名前を変更する

ALTER TYPE status_enum RENAME TO status_enum_old;

新しいタイプを作成する

CREATE TYPE status_enum AS ENUM('queued', 'running', 'done');

新しいタイプを使用するように列を更新する

ALTER TABLE job ALTER COLUMN job_status TYPE status_enum USING job_status::text::status_enum;

古いタイプを削除する

DROP TYPE status_enum_old;

このリンクは現在、503を返します
オリバー・エバンス

31

列挙型の項目を削除したい場合は、PostgreSQLのシステムテーブルを操作する必要があります。

このコマンドを使用すると、すべてのアイテムの列挙型を表示できます。

SELECT * FROM pg_enum;

次に、検索された値が一意であることを確認します。rekoruの削除中に一意性を高めるには、「enumlabel」に加えて「enumtypid」を渡す必要があります。

このコマンドは、列挙型のエントリを削除します。ここで、 'unique'は値です。

DELETE FROM pg_enum en WHERE en.enumtypid = 124 AND en.enumlabel = 'unique';

私が説明した例は、偶然にenum型に新しい値を追加したときに使用する必要がありますが、データベースのどこにも使用していません。


20
これは非常に危険な操作ですが、何をしているのかわかっている場合は、列挙型から値を削除するのは非常に迅速かつ簡単です。まず、削除する列挙値を使用しているテーブルがないことを確認します。あなたは、あなたがしますしない場合はひどく壊れ列挙型の値を参照するすべてのテーブル(戻りますようにAテーブルから例えば選択ERROR: invalid internal value for enumおよび降伏NOの結果を。)
クリントPachl

5
そうです、これは考慮すべき最も重要な側面です。私が説明した例は、偶然にenum型に新しい値を追加したときに使用する必要がありますが、データベースのどこにも使用していません。
elcudro 2015

1
このコマンドがどれほど危険であるかを考えるとDELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';、NOTEはコマンドではなく太字である必要があります。一部のテーブルで値を使用した場合、その値から回復することはできません。値を含む行を更新したり、変換したりすることはできません。行全体を削除するのが唯一の方法です。
シルヴァン

8

列挙値を変更したい人にとって、それを再作成することが唯一の実行可能で安全な解決策のようです。

一時的に列挙型列を文字列形式に変換し、列挙型を再作成してから、文字列列を列挙型に再変換します。

次に例を示します。

ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE varchar(255);
ALTER TABLE your_schema.your_table ALTER COLUMN your_column SET DEFAULT('your_default_enum_value');
DROP TYPE your_schema.your_enum_name;
CREATE TYPE your_schema.your_enum_name AS ENUM ('enum1', 'enum2', 'enum3');
ALTER TABLE your_schema.your_table ALTER your_column DROP DEFAULT;
ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;
ALTER TABLE your_schema.your_table ALTER COLUMN your_column SET DEFAULT('your_default_enum_value');

ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;する必要がありますALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_schema.your_column::your_enum_name;
マヌエルダーボー2017

6

次のクエリを使用してPostgresqlタイプからENUM値を削除します

DELETE FROM pg_enum
WHERE enumlabel = 'moderator'
AND enumtypid = ( SELECT oid FROM pg_type WHERE typname = 'admin_level1');

タイプと値の情報

DELETE FROM pg_enum
WHERE enumlabel = 'ENUM_VALUE'
AND enumtypid = ( SELECT oid FROM pg_type WHERE typname = 'ENUM_TYPE')

既存の値を他の値に変更する必要があります。そのために新しい値を追加する必要がある場合は、次を使用します。

ALTER TYPE **ENUM_TYPE** ADD VALUE '**ENUM_VALUE2**'; 

削除する前に、タイプ値を新しいタイプ値または既存の値に更新してください。


唯一の問題は、pg_typeのtypnameが小文字であることです。したがって、小文字のenum_typeを使用しない限り機能しません SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd

2

これを行うプログラム的な方法は次のとおりです。https://stackoverflow.com/a/47305844/629272に示されているのと同じ一般的な手順が適切ですが、これらの手順は、私の目的(アレンビックダウンマイグレーションの記述)にとって理にかなったものよりも手作業です。my_typemy_type_old、そしてvalue_to_delete、もちろん、必要に応じて変更する必要があります。

  1. タイプの名前を変更します。

    ALTER TYPE my_type RENAME TO my_type_old;
  2. 削除するタイプを除いて、古いタイプの値を使用して新しいタイプを作成します。

    DO $$
    BEGIN
        EXECUTE format(
            'CREATE TYPE my_type AS ENUM (%s)',
            (
                SELECT string_agg(quote_literal(value), ',')
                FROM unnest(enum_range(NULL::my_type_old)) value
                WHERE value <> 'value_to_delete'
            )
        );
    END $$;
  3. 古いタイプを使用するすべての既存の列を、新しいタイプを使用するように変更します。

    DO $$
    DECLARE
        column_data record;
        table_name varchar(255);
        column_name varchar(255);
    BEGIN
        FOR column_data IN
            SELECT cols.table_name, cols.column_name
                FROM information_schema.columns cols
                WHERE udt_name = 'my_type_old'
        LOOP
            table_name := column_data.table_name;
            column_name := column_data.column_name;
            EXECUTE format(
                '
                    ALTER TABLE %s
                    ALTER COLUMN %s
                    TYPE my_type
                    USING %s::text::my_type;
                ',
                table_name, column_name, column_name
            );
        END LOOP;
    END $$;
  4. 古いタイプを削除します。

    DROP TYPE my_type_old;

0

データセットがそれほど大きくない場合は--column-inserts、テキストエディターでダンプを編集してダンプし、値を削除してダンプを再インポートできます。


0

v.10でも同じ問題がありました。postgres。削除には特定の手順が必要であり、順序が正しくない場合、テーブルが読み取り用にロックされる可能性さえあります。

削除するのに便利なスクリプトを書きました。すでに数回その性能を証明しました。ただし、この手順では、削除された値を新しい値に置き換える必要があります(テーブルフィールドで許可されている場合は、NULLにすることができます)。

使用するには、3つの値を入力するだけです。

DO $$
DECLARE
    enumTypeName VARCHAR := 'enum_name'; -- VALUE #1, set yor value!
    enumOldFieldValue varchar := 'old_enum_value'; -- VALUE #2, enum value which have to be deleted
    enumNewFieldValue varchar := null; -- VALUE #3, which new value must be instead of deleted
    sql varchar:='';
    rec record;
BEGIN
    raise info 'Check on old and new enum values.';
    IF exists(select * FROM pg_enum -- check existing of OLD enum value
              WHERE enumtypid = (select oid from pg_type where typName=cast(enumTypeName as varchar) limit 1) and enumlabel=cast(enumOldFieldValue as varchar))
      AND
       (exists(select *
               FROM pg_enum -- check existing of NEW enum value
               WHERE enumtypid = (select oid from pg_type where typName = cast(enumTypeName as varchar) limit 1)
                 and enumlabel = cast(enumNewFieldValue as varchar))
           OR
        enumNewFieldValue IS NULL)
        THEN
            raise info 'Check passed!';

            -- selecting all tables with schemas which has column with enum relation
            create temporary table tmp_table_names
             as SELECT concat(c.table_schema,'.',c.table_name ) as table_name, c.column_name
                FROM information_schema.columns c
                WHERE c.udt_name = cast(enumTypeName as varchar)
                  and c.table_schema=c.udt_schema and data_type = 'USER-DEFINED';

            -- if we have table(s) that uses such enum
            if exists(select * from tmp_table_names)
                then
                    FOR rec in (select table_name, column_name from tmp_table_names) LOOP
                        sql:= format('UPDATE %1$s set %2$s = %3$L where %2$s=%4$L',rec.table_name, rec.column_name, enumNewFieldValue, enumOldFieldValue);
                        raise info 'Update by looping: %', sql;
                        EXECUTE sql;
                    END LOOP;
            end if;

            -- just after changing all old values in all tables we can delete old enum value
            sql := format('DELETE FROM pg_enum WHERE enumtypid = (select oid from pg_type where typName=%1$L limit 1) and enumlabel=%2$L',enumTypeName,enumOldFieldValue);
            raise info 'Delete enum value: %', sql;
            EXECUTE sql;

            drop table  tmp_table_names;
        ELSE
            raise info 'Old or new enum values is missing.';
    end if;
END $$;
  1. リストアイテム

-1

ENUMから個々の値を削除することはできません。唯一の可能な解決策は、必要な値でENUMを削除して再作成することです。


それは非常に可能です、あなたがおそらく意味したことは「公式にサポートされていない」ことです。
Rikudou_Sennin

@Rikudou_Senninは、ENUMから1つの正確な値を削除できるコードを提供してもよろしいですか?
Zaytsev Dmitry

2
@ZaytsevDmitryこちらです:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
ローマポドリノフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.