Postgres'te bir enum türü değeri nasıl silinir?


109

Postgresql'de oluşturduğum bir enum türü değerini nasıl silerim?

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

Örneğin moderatorlisteden çıkarmak istiyorum .

Belgelerde hiçbir şey bulamıyorum.

Postgresql 9.3.4 kullanıyorum.


4
drop type admin_level1?
bereal

1
Temel kural: her create xxxşey içindrop xxx
a_horse_with_no_name

IMO seçilen cevap başka bir cevapla değiştirilmelidir.
Roman Podlinov

Yanıtlar:


180

Diğer türler gibi enum türlerini silersiniz (drop) DROP TYPE:

DROP TYPE admin_level1;

Bir enum türünden tek bir değeri nasıl kaldıracağınızı gerçekten sormanız mümkün mü ? Eğer öyleyse, yapamazsınız. Desteklenmiyor :

Türler enumöncelikle statik değer kümeleri için tasarlanmış olsa da , mevcut bir numaralandırma türüne yeni değerler eklemek ve değerleri yeniden adlandırmak için destek vardır (bkz. ALTER TYPE). Varolan değerler bir enum türünden kaldırılamaz ve bu tür değerlerin sıralama düzeni, enum türünün kaldırılması ve yeniden oluşturulması dışında değiştirilemez.

Değeri olmayan yeni bir tür oluşturmalı, eski türün tüm mevcut kullanımlarını yeni türü kullanmak için dönüştürmeli ve ardından eski türü bırakmalısınız.

Örneğin

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
Bu harika! Bununla Alembic göç problemini çözmeyi başardım. Çünkü yeni numaralama türü ekleme olamazdı(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan

disable_ddl_transaction ekleyin! taşıma dosyasının en üstüne.
chell

Blah WHERE güç = 'tanrı'; benim durumumda çalışmıyor
ankit

1
TBH Bu cevabın neden seçildiğini anlamıyorum. Bu cevap doğru değil! Belirtilen etiketle pg_enum'dan değeri silebilirsiniz.
Roman Podlinov

2
@RomanPoelinov doğrudan katalog manipülasyonu riski size aittir. Postgres'in enum değerlerini yerel olarak silmeyi desteklememesinin nedenleri vardır. Desteklenmeyen ve güvenli olmayan bir katalog saldırısına kıyasla bu nasıl "doğru değil"?
Craig Ringer

41

Burada çok iyi yazılmış:

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

mevcut türü yeniden adlandırın

ALTER TYPE status_enum RENAME TO status_enum_old;

yeni türü yarat

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

yeni türü kullanmak için sütunları güncelleyin

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

eski türü kaldır

DROP TYPE status_enum_old;

Bu bağlantı şimdi 503 döndürüyor.
Oliver Evans

32

Enum türündeki öğeyi silmek istiyorsanız, PostgreSQL'in sistem tablosunda işlem yapmalısınız.

Bu komutla, tüm öğe numaralandırma türünü görüntüleyebilirsiniz.

Pg_enum'DAN SEÇİN *;

Ardından, aranan değerin benzersiz olup olmadığını kontrol edin. Rekoru'nun çıkarılması sırasında benzersizliği artırmak için, "enumlabel" e ek olarak "enumtypid" geçirilmelidir.

Bu komut, enum türündeki girişi kaldırır, burada 'benzersiz' sizin değerinizdir.

Pg_enum'DAN SİLİN en.enumtypid = 124 VE en.enumlabel = 'benzersiz';

NOT Açıkladığım örnek, tesadüfen enum tipine yeni bir değer eklediğimizde ve yine de onu veritabanının hiçbir yerinde kullanmadığımızda kullanılmalıdır.


20
Bu çok tehlikeli bir işlemdir , ancak ne yaptığınızı biliyorsanız, enum türünden bir değeri kaldırmak çok hızlı ve özlüdür. Öncelikle, kaldırmak istediğiniz enum değerini hiçbir tablonun kullanmadığından emin olun. Bunu yapmazsanız , enum değerine başvuran tüm tabloları kötü bir şekilde kırarsınız (örneğin, böyle bir tablodan seçmek geri dönecek ERROR: invalid internal value for enumve HAYIR sonuç verecektir .)
Clint Pachl

5
Doğru, dikkate alınması gereken en önemli husus budur. Tanımladığım örnek, tesadüfen enum tipine yeni bir değer eklediğimizde ve yine de onu veritabanının hiçbir yerinde kullanmadığımızda kullanılmalıdır.
elcudro

1
Bu komutun ne kadar tehlikeli olduğu göz önüne alındığında DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';NOT, komut değil BOLD olmalıdır. Bazı tablodaki değeri kullandıysanız, ondan kurtaramazsınız. Değeri içeren satırları güncelleyemezsiniz, dönüştüremezsiniz. Tek yol tüm satırı silmektir.
Sylvain

8

Enum değerlerini değiştirmek isteyenler için, bunu yeniden oluşturmak, uygulanabilir ve güvenli tek çözüm gibi görünüyor.

Enum sütununu geçici olarak bir dize biçimine dönüştürmekten, numaralandırmayı yeniden oluşturmaktan ve ardından dize sütununu enum türüne geri döndürmekten oluşur.

İşte bir örnek:

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;olmalı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

Postgresql türünden ENUM değerini silmek için aşağıdaki sorguyu kullanın

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

Neyin türü ve değeri hakkında bilgi

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

Mevcut değerleri diğerleriyle değiştirmelisiniz. Bunun için yeni bir değer eklemeniz gerekiyorsa, şunu kullanın:

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

Silmeden önce, tip değerini yeni tip değerine veya mevcut değere güncelleyin.


Tek sorun, pg_type'daki tip adının küçük harf olmasıdır. bu nedenle küçük harf enum_type inSELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd

2

Bunu yapmanın programlı yolu aşağıdaki gibidir. Https://stackoverflow.com/a/47305844/629272'de verilen genel adımlar uygundur, ancak bunlar benim amaçlarım için mantıklı olmaktan çok daha manüeldir (alembic aşağı geçiş yazmak). my_type, my_type_oldve value_to_deletetabii ki uygun şekilde değiştirilmelidir.

  1. Türünüzü yeniden adlandırın.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Eski türünüzdeki değerlerle, silmek istediğinizi hariç tutarak yeni bir tür oluşturun.

    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. Yenisini kullanmak için eski türü kullanan tüm mevcut sütunları değiştirin.

    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. Eski türü silin.

    DROP TYPE my_type_old;

0

Veri kümeniz çok büyük değilse --column-inserts, dökümü bir metin düzenleyiciyle düzenleyerek dökebilir, değeri kaldırabilir ve dökümü yeniden içe aktarabilirsiniz


0

Aynı sorunu v.10'da yaşadım. postgres. Silme belirli prosedürler gerektirir ve sıra doğru değilse, tablonun okuma için kilitlenme şansı bile olacaktır.

Silmek için uygun bir komut dosyası yazdı. Performansının birkaç katını zaten kanıtladı. Ancak bu prosedür, silinen değeri yenisiyle değiştirmeyi içerir (tablo alanı buna izin veriyorsa NULL olabilir).

Kullanmak için 3 değer girmeniz yeterlidir.

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. Liste öğesi

-1

ENUM'dan tek tek değer silmek mümkün değildir, tek olası çözüm DROP ve ENUM'u gerekli değerlerle yeniden oluşturmaktır.


Bu çok olası, muhtemelen "resmi olarak desteklenmiyor" demek istediniz.
Rikudou_Sennin

@Rikudou_Sennin, ENUM'dan tek bir tam değeri kaldırabilecek bir kod sağlar mısınız?
Zaytsev Dmitry

2
@ZaytsevDmitry işte buradasınız:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.