Làm thế nào để xóa một giá trị kiểu enum trong postgres?


109

Làm cách nào để xóa một giá trị kiểu enum mà tôi đã tạo trong postgresql?

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

Vd: Tôi muốn xóa moderatorkhỏi danh sách.

Tôi dường như không thể tìm thấy bất cứ điều gì trên tài liệu.

Tôi đang sử dụng Postgresql 9.3.4.


4
drop type admin_level1?
bereal

1
Nguyên tắc hàng đầu: cho mỗi create xxxcó mộtdrop xxx
a_horse_with_no_name

IMO câu trả lời đã chọn phải được đổi thành câu trả lời khác.
Roman Podlinov

Câu trả lời:


180

Bạn xóa (thả) các loại enum giống như bất kỳ loại nào khác, với DROP TYPE:

DROP TYPE admin_level1;

Có thể bạn đang thực sự hỏi về cách xóa một giá trị riêng lẻ khỏi một kiểu enum ? Nếu vậy, bạn không thể. Nó không được hỗ trợ :

Mặc dù enumcác kiểu chủ yếu dành cho các tập giá trị tĩnh, nhưng có sự hỗ trợ để thêm giá trị mới vào kiểu enum hiện có và để đổi tên các giá trị (xem ALTER TYPE). Các giá trị hiện có không thể bị xóa khỏi kiểu enum, cũng như không thể thay đổi thứ tự sắp xếp của các giá trị đó, thiếu sót và tạo lại kiểu enum.

Bạn phải tạo một loại mới không có giá trị, chuyển đổi tất cả các mục đích sử dụng hiện có của loại cũ sang sử dụng loại mới, sau đó bỏ loại cũ.

Ví dụ

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
Điều này là tuyệt vời! Với điều này, tôi đã giải quyết được vấn đề di cư Alembic. Tôi không thể thêm kiểu enum mới vì(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan

thêm disable_ddl_transaction! lên đầu tệp di chuyển.
chell

XÓA TỪ blah WHERE power = 'god'; không hoạt động trong trường hợp của tôi
ankit

1
TBH Tôi không hiểu tại sao câu trả lời này lại được chọn. Câu trả lời này không đúng! Bạn có thể xóa giá trị khỏi pg_enum với nhãn được chỉ định.
Roman Podlinov

2
@RomanPoelinov thao tác danh mục trực tiếp, tôi tự chịu rủi ro. Có những lý do khiến postgres không hỗ trợ xóa các giá trị enum nguyên bản. Làm thế nào điều này "không đúng" so với một vụ hack danh mục không được hỗ trợ và không an toàn?
Craig Ringer

41

Rất hay được viết ở đây:

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

đổi tên loại hiện có

ALTER TYPE status_enum RENAME TO status_enum_old;

tạo kiểu mới

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

cập nhật các cột để sử dụng kiểu mới

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

loại bỏ loại cũ

DROP TYPE status_enum_old;

Liên kết này hiện trả về 503.
Oliver Evans

32

Nếu bạn muốn xóa mục kiểu enum, bạn phải thao tác trên bảng hệ thống của PostgreSQL.

Với lệnh này, bạn có thể hiển thị tất cả các mục kiểu enum.

CHỌN * TỪ pg_enum;

Sau đó, kiểm tra xem giá trị đã tìm kiếm là duy nhất. Để tăng tính độc đáo trong quá trình loại bỏ rekoru, bạn phải thông qua 'enumtypid' cùng với 'enumlabel'.

Lệnh này xóa mục nhập trong kiểu enum, trong đó 'duy nhất' là giá trị của bạn.

XÓA TỪ pg_enum en WHERE vi.enumtypid = 124 AND en.enumlabel = 'unique';

LƯU Ý Ví dụ mà tôi đã mô tả phải được sử dụng, khi tình cờ chúng tôi thêm giá trị mới vào kiểu enum, nhưng chúng tôi chưa sử dụng nó ở bất kỳ đâu trong cơ sở dữ liệu.


20
Đây là một thao tác rất nguy hiểm , nhưng nó rất nhanh chóng và ngắn gọn trong việc xóa một giá trị khỏi kiểu enum nếu bạn biết mình đang làm gì. Đầu tiên, hãy đảm bảo rằng không có bảng nào đang sử dụng giá trị enum mà bạn muốn xóa. Nếu bạn không, bạn sẽ tồi tệ phá vỡ tất cả các bảng tham chiếu giá trị enum (ví dụ như lựa chọn từ một bảng như vậy sẽ trở lại ERROR: invalid internal value for enumvà mang NO kết quả.)
Clint Pachl

5
Đúng vậy, đây là khía cạnh quan trọng nhất cần được tính đến. Ví dụ mà tôi đã mô tả phải được sử dụng, khi tình cờ chúng tôi thêm giá trị mới vào kiểu enum, nhưng chúng tôi chưa sử dụng nó ở bất kỳ đâu trong cơ sở dữ liệu.
elcudro

1
Với mức độ nguy hiểm của lệnh này, DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';LƯU Ý phải ở dạng BOLD, không phải lệnh. Nếu bạn đã sử dụng giá trị trong một số bảng, bạn không thể khôi phục từ nó. Bạn không thể cập nhật các hàng có chứa giá trị, bạn không thể chuyển đổi. Cách duy nhất là xóa toàn bộ hàng.
Sylvain

8

Đối với những người muốn sửa đổi các giá trị enum, tạo lại nó dường như là giải pháp khả thi và an toàn duy nhất.

Nó bao gồm chuyển đổi tạm thời cột enum sang định dạng chuỗi, tạo lại enum và sau đó chuyển đổi lại cột chuỗi trở lại kiểu enum.

Đây là một ví dụ:

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;nênALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_schema.your_column::your_enum_name;
Manuel Darveau

6

Sử dụng truy vấn sau để Xóa giá trị ENUM khỏi loại Postgresql

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

Chỉ cần thông tin về loại và giá trị

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

Bạn nên thay đổi các giá trị hiện có thành giá trị khác. Vì vậy, nếu bạn cần thêm giá trị mới, hãy sử dụng:

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

Trước khi xóa, hãy cập nhật giá trị kiểu thành giá trị kiểu mới hoặc giá trị hiện có.


Vấn đề duy nhất là tên kiểu chữ trong pg_type là chữ thường. nên nó không làm việc, trừ khi sử dụng các enum_type chữ thường trong SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd

2

Cách lập trình để thực hiện việc này như sau. Các bước chung tương tự như được đưa ra trong https://stackoverflow.com/a/47305844/629272 là phù hợp, nhưng những bước đó thủ công hơn là phù hợp với mục đích của tôi (viết một di chuyển xuống alembic). my_type, my_type_oldvalue_to_delete, tất nhiên, nên được thay đổi khi thích hợp.

  1. Đổi tên loại của bạn.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Tạo một kiểu mới với các giá trị từ kiểu cũ của bạn, ngoại trừ giá trị bạn muốn xóa.

    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. Thay đổi tất cả các cột hiện có sử dụng loại cũ sang sử dụng cột mới.

    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. Xóa loại cũ.

    DROP TYPE my_type_old;

0

nếu tập dữ liệu của bạn không quá lớn, bạn có thể kết xuất bằng cách --column-insertschỉnh sửa kết xuất bằng trình soạn thảo văn bản, xóa giá trị và nhập lại kết xuất


0

Gặp vấn đề tương tự trong câu 10. bưu điện. Việc xóa yêu cầu một số quy trình nhất định và nếu trình tự không chính xác, thì thậm chí sẽ có khả năng bảng bị khóa để đọc.

Đã viết một kịch bản thuận tiện để xóa. Đã được chứng minh nhiều lần hiệu suất của nó. Tuy nhiên, thủ tục này liên quan đến việc thay thế giá trị đã xóa bằng một giá trị mới (nó có thể là NULL nếu trường bảng cho phép điều này).

Để sử dụng, bạn chỉ cần điền vào 3 giá trị.

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. Liệt kê mục

-1

Không thể xóa giá trị riêng lẻ khỏi ENUM, giải pháp khả thi duy nhất là DROP và tạo lại ENUM với các giá trị cần thiết.


Rất có thể, những gì bạn có thể muốn nói là "không được hỗ trợ chính thức".
Rikudou_Sennin

@Rikudou_Sennin, bạn có vui lòng cung cấp mã có thể xóa một giá trị chính xác khỏi ENUM không?
Zaytsev Dmitry

2
@ZaytsevDmitry đây bạn:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.