Bagaimana cara menghapus nilai tipe enum di postgres?

109

Bagaimana cara menghapus nilai jenis enum yang saya buat di postgresql?

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

Misalnya saya ingin menghapus moderatordari daftar.

Sepertinya saya tidak dapat menemukan apa pun di dokumen.

Saya menggunakan Postgresql 9.3.4.

Amjith
sumber
4
drop type admin_level1?
bereal
1
Aturan praktis: untuk setiap create xxxadadrop xxx
a_horse_with_no_name
IMO jawaban yang dipilih harus diubah ke yang lain.
Roman Podlinov

Jawaban:

180

Anda menghapus (menjatuhkan) jenis enum seperti jenis lainnya, dengan DROP TYPE:

DROP TYPE admin_level1;

Apakah mungkin Anda benar-benar bertanya tentang cara menghapus nilai individual dari jenis enum ? Jika demikian, Anda tidak bisa. Itu tidak didukung :

Meskipun enumtipe terutama ditujukan untuk kumpulan nilai statis, ada dukungan untuk menambahkan nilai baru ke tipe enum yang ada, dan untuk mengganti nama nilai (lihat ALTER TYPE). Nilai yang sudah ada tidak dapat dihapus dari jenis enum, pengurutan pengurutan nilai-nilai tersebut juga tidak dapat diubah, selain menghilangkan dan membuat ulang jenis enum.

Anda harus membuat tipe baru tanpa nilai, mengonversi semua penggunaan yang ada dari tipe lama untuk menggunakan tipe baru, lalu membuang tipe lama.

Misalnya

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;
Craig Ringer
sumber
1
Ini luar biasa! Dengan ini saya berhasil menyelesaikan masalah migrasi Alembic. Saya tidak bisa menambahkan jenis enum baru karena(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan
tambahkan disable_ddl_transaction! ke bagian atas file migrasi.
chell
HAPUS DARI bla DI MANA power = 'tuhan'; tidak berfungsi dalam kasus saya
ankit
1
TBH Saya tidak mengerti mengapa jawaban ini dipilih. Jawaban ini tidak benar! Anda dapat menghapus nilai dari pg_enum dengan label tertentu.
Roman Podlinov
2
@RomanPoelinov manipulasi katalog langsung Saya akan menanggung risiko Anda sendiri. Ada alasan postgres tidak mendukung penghapusan nilai enum secara native. Bagaimana hal ini "tidak benar" dibandingkan dengan peretasan katalog yang tidak didukung dan tidak aman?
Craig Ringer
41

Ditulis dengan sangat baik di sini:

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

ganti nama tipe yang ada

ALTER TYPE status_enum RENAME TO status_enum_old;

buat tipe baru

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

perbarui kolom untuk menggunakan tipe baru

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

hapus tipe lama

DROP TYPE status_enum_old;
dnaik
sumber
Tautan ini sekarang mengembalikan 503.
Oliver Evans
32

Jika Anda ingin menghapus item dengan tipe enum, Anda harus beroperasi pada tabel sistem PostgreSQL.

Dengan perintah ini, Anda dapat menampilkan semua item jenis enum.

PILIH * DARI pg_enum;

Kemudian periksa apakah nilai yang dicari itu unik. Untuk meningkatkan keunikan selama penghapusan recordu harus melewati 'enumtypid' selain 'enumlabel'.

Perintah ini menghapus entri dalam tipe enum, di mana 'unik' adalah nilai Anda.

HAPUS DARI pg_enum id DI MANA en.enumtypid = 124 DAN en.enumlabel = 'unique';

CATATAN Contoh yang saya jelaskan harus digunakan, ketika secara kebetulan kami menambahkan nilai baru ke tipe enum, namun kami belum menggunakannya di mana pun dalam database.

elcudro
sumber
20
Ini adalah operasi yang sangat berbahaya , tetapi sangat cepat dan ringkas dalam menghapus nilai dari jenis enum jika Anda tahu apa yang Anda lakukan. Pertama, pastikan tidak ada tabel yang menggunakan nilai enum yang ingin Anda hapus. Jika tidak, Anda akan merusak semua tabel yang mereferensikan nilai enum dengan buruk (misalnya memilih dari tabel seperti itu akan mengembalikan ERROR: invalid internal value for enumdan menghasilkan NO hasil.)
Clint Pachl
5
Benar sekali, ini adalah aspek terpenting yang harus diperhatikan. Contoh yang saya jelaskan harus digunakan, ketika secara kebetulan kita menambahkan nilai baru ke tipe enum, namun kita belum menggunakannya di mana pun di database.
elcudro
1
Mengingat betapa berbahayanya perintah ini DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';CATATAN harus dalam BOLD, bukan perintah. Jika Anda telah menggunakan nilai di beberapa tabel, Anda tidak dapat memulihkannya. Anda tidak dapat memperbarui baris yang berisi nilai, Anda tidak dapat mengonversi. Satu-satunya cara adalah menghapus seluruh baris.
Sylvain
8

Bagi mereka yang ingin mengubah nilai enum, membuat ulang tampaknya menjadi satu-satunya solusi yang layak dan aman.

Ini terdiri dari konversi sementara kolom enum ke format string, membuat ulang enum dan kemudian mengubah kolom string kembali ke tipe enum.

Berikut ini contohnya:

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');
sveilleux2
sumber
ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;harusALTER 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

Gunakan query berikut untuk menghapus nilai ENUM dari tipe Postgresql

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

Sekadar info untuk apa tipe dan apa nilainya

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

Anda harus mengubah nilai yang ada ke yang lain. Untuk itu jika perlu menambahkan nilai baru, maka gunakan:

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

Sebelum menghapus, perbarui nilai tipe ke nilai tipe baru atau nilai yang sudah ada.

Somnath Muluk
sumber
Satu-satunya masalah adalah nama ketik di pg_type menggunakan huruf kecil. jadi tidak berfungsi, kecuali menggunakan huruf kecil enum_type di SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd
2

Cara terprogram untuk melakukannya adalah sebagai berikut. Langkah-langkah umum yang sama seperti yang diberikan di https://stackoverflow.com/a/47305844/629272 sesuai, tetapi itu lebih manual daripada yang masuk akal untuk tujuan saya (menulis migrasi alembic ke bawah). my_type,, my_type_olddan value_to_delete, tentu saja, harus diubah sebagaimana mestinya.

  1. Ubah nama tipe Anda.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Buat tipe baru dengan nilai dari tipe lama Anda, tidak termasuk yang ingin Anda hapus.

    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. Ubah semua kolom yang ada yang menggunakan tipe lama untuk menggunakan yang baru.

    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. Hapus tipe lama.

    DROP TYPE my_type_old;
Orang California
sumber
0

Jika kumpulan data Anda tidak terlalu besar, Anda dapat membuangnya dengan --column-insertsmengedit dump dengan editor teks, hapus nilainya dan impor kembali dump

sherpya
sumber
0

Punya masalah yang sama di v.10. postgres. Penghapusan memerlukan prosedur tertentu, dan jika urutannya tidak benar, maka ada kemungkinan tabel dikunci untuk dibaca.

Menulis skrip yang mudah dihapus. Sudah beberapa kali terbukti performanya. Namun, prosedur ini melibatkan penggantian nilai yang dihapus dengan yang baru (bisa NULL jika bidang tabel memungkinkan ini).

Untuk menggunakannya, Anda hanya perlu mengisi 3 nilai.

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. Daftar barang
SergioBazileyroElMurdoMendez
sumber
-1

Tidak mungkin menghapus nilai individu dari ENUM, satu-satunya solusi yang mungkin adalah DROP dan membuat ulang ENUM dengan nilai yang diperlukan.

Zaytsev Dmitry
sumber
Sangat mungkin, yang mungkin Anda maksud adalah "tidak didukung secara resmi".
Rikudou_Sennin
@Rikudou_Sennin maukah Anda memberikan kode yang dapat menghapus satu nilai persis dari ENUM?
Zaytsev Dmitry
2
@ZaytsevDmitry ini dia:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov