จะลบค่าประเภท enum ใน postgres ได้อย่างไร?


109

ฉันจะลบค่าประเภท enum ที่ฉันสร้างใน 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 คำตอบที่เลือกต้องเปลี่ยนเป็นคำตอบอื่น
Roman Podlinov

คำตอบ:


180

คุณลบ (วาง) ประเภท enum เหมือนกับประเภทอื่น ๆ ด้วยDROP TYPE:

DROP TYPE admin_level1;

มันเป็นไปได้ที่คุณจริงถามเกี่ยวกับวิธีการลบค่าบุคคลจากประเภท enum ? ถ้าเป็นเช่นนั้นคุณทำไม่ได้ ไม่รองรับ :

แม้ว่าenumประเภทจะมีจุดประสงค์หลักสำหรับชุดค่าคงที่ แต่ก็มีการสนับสนุนสำหรับการเพิ่มค่าใหม่ให้กับประเภท enum ที่มีอยู่และสำหรับการเปลี่ยนชื่อค่า (ดูALTER TYPE) ค่าที่มีอยู่ไม่สามารถลบออกจากชนิด enum ได้และไม่สามารถเปลี่ยนแปลงลำดับการเรียงของค่าดังกล่าวได้โดยไม่ต้องวางและสร้างประเภท enum ขึ้นมาใหม่

คุณต้องสร้างประเภทใหม่โดยไม่มีค่าแปลงการใช้งานประเภทเก่าทั้งหมดที่มีอยู่เพื่อใช้ประเภทใหม่จากนั้นจึงปล่อยประเภทเก่า

เช่น

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 ได้ ฉันไม่สามารถเพิ่ม enum type ใหม่ได้เนื่องจาก(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan

เพิ่ม disable_ddl_transaction! ไปที่ด้านบนของไฟล์การย้ายข้อมูล
chell

ลบออกจาก blah ที่อำนาจ = 'พระเจ้า'; ไม่ทำงานในกรณีของฉัน
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

ด้วยคำสั่งนี้คุณสามารถแสดงประเภท enum รายการทั้งหมด

เลือก * จาก pg_enum;

จากนั้นตรวจสอบว่าค่าที่ค้นหานั้นไม่ซ้ำกัน เพื่อเพิ่มความเป็นเอกลักษณ์ระหว่างการกำจัด rekoru จะต้องผ่าน 'enumtypid' นอกเหนือจาก 'enumlabel'

คำสั่งนี้จะลบรายการในประเภท enum โดยที่ 'unique' คือค่าของคุณ

ลบจาก pg_enum en WHERE en.enumtypid = 124 AND en.enumlabel = 'unique';

หมายเหตุ ต้องใช้ตัวอย่างที่ฉันอธิบายไว้เมื่อเราเพิ่มค่าใหม่ให้กับประเภท enum โดยบังเอิญ แต่เรายังไม่ได้ใช้ที่ใดในฐานข้อมูล


20
นี่เป็นการดำเนินการที่อันตรายมากแต่มันรวดเร็วและรวบรัดมากในการลบค่าออกจากประเภท enum หากคุณรู้ว่าคุณกำลังทำอะไรอยู่ ขั้นแรกตรวจสอบให้แน่ใจว่าไม่มีตารางใดใช้ค่า enum ที่คุณต้องการลบ ถ้าคุณไม่ทำคุณจะทำลายตารางทั้งหมดที่อ้างอิงค่า enum ไม่ดี (เช่นการเลือกจากตารางดังกล่าวจะส่งคืนERROR: invalid internal value for enumและให้ผลลัพธ์NO )
Clint Pachl

5
ถูกต้องนี่คือสิ่งที่สำคัญที่สุดที่ควรนำมาพิจารณา ต้องใช้ตัวอย่างที่ฉันอธิบายไว้เมื่อเราเพิ่มค่าใหม่ให้กับประเภท enum โดยบังเอิญ แต่เรายังไม่ได้ใช้ที่ใดในฐานข้อมูล
elcudro

1
ระบุว่าคำสั่งนี้อันตรายเพียงใดDELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';NOTE ควรอยู่ใน BOLD ไม่ใช่คำสั่ง หากคุณใช้ค่าในบางตารางคุณจะไม่สามารถกู้คืนได้ คุณไม่สามารถอัปเดตแถวที่มีค่าคุณไม่สามารถแปลงได้ วิธีเดียวคือลบทั้งแถว
Sylvain

8

สำหรับผู้ที่ต้องการแก้ไขค่า enum การสร้างใหม่ดูเหมือนจะเป็นวิธีเดียวที่ใช้ได้ผลและปลอดภัย

ประกอบด้วยในการแปลงคอลัมน์ enum เป็นรูปแบบสตริงชั่วคราวสร้าง enum ใหม่แล้วแปลงคอลัมน์สตริงกลับเป็นชนิด enum

นี่คือตัวอย่าง:

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;
Manuel Darveau

7

ใช้แบบสอบถามต่อไปนี้เพื่อลบค่า ENUM จากประเภท Postgresql

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**'; 

ก่อนที่จะลบให้อัปเดตค่าประเภทเป็นค่าประเภทใหม่หรือค่าที่มีอยู่


ปัญหาเดียวคือ typname ใน pg_type เป็นตัวพิมพ์เล็ก ดังนั้นจึงไม่ทำงานเว้นแต่จะใช้ตัวพิมพ์เล็ก enum_type ใน SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd

2

วิธีการแบบเป็นโปรแกรมในการดำเนินการมีดังนี้ ขั้นตอนทั่วไปเช่นเดียวกับที่ระบุในhttps://stackoverflow.com/a/47305844/629272นั้นเหมาะสม แต่เป็นขั้นตอนแบบแมนนวลมากกว่าที่เหมาะสมสำหรับจุดประสงค์ของฉัน (การเขียนการย้ายข้อมูลแบบไม่ใช้สัญญาณ) 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

มีปัญหาเดียวกันในเวอร์ชัน 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 ได้วิธีเดียวที่เป็นไปได้คือการ DROP และสร้าง ENUM ใหม่ด้วยค่าที่จำเป็น


เป็นไปได้มากสิ่งที่คุณอาจหมายถึงคือ "ไม่ได้รับการสนับสนุนอย่างเป็นทางการ"
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.