Postgres에서 열거 형 값을 삭제하는 방법은 무엇입니까?


109

postgresql에서 생성 한 열거 형 값을 어떻게 삭제합니까?

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

예 : moderator목록에서 제거하고 싶습니다 .

문서에서 아무것도 찾을 수없는 것 같습니다.

Postgresql 9.3.4를 사용하고 있습니다.


4
drop type admin_level1?
bereal 2014-09-12

1
엄지 손가락의 규칙 : 모든위한 create xxxdrop xxx
a_horse_with_no_name가

IMO 선택한 답변을 다른 답변으로 변경해야합니다.
Roman Podlinov

답변:


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을 추가하십시오! 마이그레이션 파일의 맨 위로.
chell

어쩌다 전원 = '신'에서 삭제; 내 경우에는 작동하지 않습니다
ANKIT

1
TBH이 답변이 선택된 이유를 이해할 수 없습니다. 이 대답은 정확하지 않습니다! 지정된 레이블로 pg_enum에서 값을 삭제할 수 있습니다.
Roman Podlinov

2
@RomanPoelinov 직접 카탈로그 조작 나는 당신의 책임하에 있습니다. postgres가 기본적으로 enum 값 삭제를 지원하지 않는 이유가 있습니다. 지원되지 않고 안전하지 않은 카탈로그 해킹과 비교하여 이것이 "올바르지 않은"이유는 무엇입니까?
Craig Ringer

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을 반환합니다.
Oliver Evans

32

enum 유형의 항목을 삭제하려면 PostgreSQL의 시스템 테이블에서 작업해야합니다.

이 명령을 사용하면 모든 항목 열거 유형을 표시 할 수 있습니다.

SELECT * FROM pg_enum;

그런 다음 검색된 값이 고유한지 확인하십시오. rekoru를 제거하는 동안 고유성을 높이려면 'enumlabel'외에도 'enumtypid'를 전달해야합니다.

이 명령은 enum 유형의 항목을 제거합니다. 여기서 'unique'는 값입니다.

pg_enum에서 삭제 en WHERE en.enumtypid = 124 AND en.enumlabel = 'unique';

참고 내가 설명한 예제는 우연히 열거 형 유형에 새 값을 추가했지만 데이터베이스의 어느 곳에서도 사용하지 않았을 때 사용해야합니다.


20
이것은 매우 위험한 작업 이지만 수행중인 작업을 알고있는 경우 열거 형 유형에서 값을 제거하는 것은 매우 빠르고 간결합니다. 먼저 제거하려는 열거 형 값을 사용하는 테이블이 없는지 확인합니다. 당신은, 당신은 할 수없는 경우 심하게 파괴 열거 값을 참조하는 모든 테이블 (반환 같은 테이블에서 예를 선택 ERROR: invalid internal value for enum하고 양보 NO의 결과를.)
클린트 Pachl

5
맞습니다. 이것은 고려해야 할 가장 중요한 측면입니다. 우연히 enum 유형에 새 값을 추가했지만 데이터베이스의 어느 곳에서도 사용하지 않은 경우 내가 설명한 예제를 사용해야합니다.
elcudro 2015-09-23

1
이 명령이 얼마나 위험한지를 감안할 때 DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';NOTE는 명령이 아니라 굵게 표시되어야합니다. 일부 테이블의 값을 사용한 경우 복구 할 수 없습니다. 값이 포함 된 행은 업데이트 할 수 없으며 변환 할 수 없습니다. 유일한 방법은 전체 행을 삭제하는 것입니다.
Sylvain

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;
마누엘 Darveau에게

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에 제공된 것과 동일한 일반 단계 가 적절하지만 내 목적에 맞는 것보다 수동입니다 (Alembic 다운 마이그레이션 작성). my_type, my_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을 DROP하고 다시 만드는 것입니다.


아마도 "공식적으로 지원되지 않는다"는 의미 일 수 있습니다.
Rikudou_Sennin

@Rikudou_Sennin ENUM에서 하나의 정확한 값을 제거 할 수있는 코드를 제공 하시겠습니까?
Zaytsev Dmitry

2
@ZaytsevDmitry 여기 있습니다 :DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.