CATATAN jika Anda menggunakan PostgreSQL 9.1 atau lebih baru, dan Anda setuju dengan perubahan di luar transaksi, lihat jawaban ini untuk pendekatan yang lebih sederhana.
Saya memiliki masalah yang sama beberapa hari yang lalu dan menemukan posting ini. Jadi jawaban saya dapat bermanfaat bagi seseorang yang mencari solusi :)
Jika Anda hanya memiliki satu atau dua kolom yang menggunakan tipe enum yang ingin Anda ubah, Anda dapat mencoba ini. Anda juga dapat mengubah urutan nilai dalam tipe baru.
-- 1. rename the enum type you want to change
alter type some_enum_type rename to _some_enum_type;
-- 2. create new type
create type some_enum_type as enum ('old', 'values', 'and', 'new', 'ones');
-- 3. rename column(s) which uses our enum type
alter table some_table rename column some_column to _some_column;
-- 4. add new column of new type
alter table some_table add some_column some_enum_type not null default 'new';
-- 5. copy values to the new column
update some_table set some_column = _some_column::text::some_enum_type;
-- 6. remove old column and type
alter table some_table drop column _some_column;
drop type _some_enum_type;
3-6 harus diulang jika ada lebih dari 1 kolom.
ALTER TYPE
. Tetapi bahkan sebelum itu,ALTER TABLE foo ALTER COLUMN bar TYPE new_type USING bar::text::new_type;
jauh lebih unggul.ALTER TABLE some_table ALTER COLUMN some_column TYPE some_enum_type USING some_column::text::some_enum_type;
PostgreSQL 9.1 memperkenalkan kemampuan untuk mengubah tipe ENTER:
sumber
Solusi yang mungkin adalah sebagai berikut; prasyaratnya adalah, bahwa tidak ada konflik dalam nilai enum yang digunakan. (mis. saat menghapus nilai enum, pastikan bahwa nilai ini tidak digunakan lagi.)
Juga dengan cara ini urutan kolom tidak akan diubah.
sumber
pg_enum
yang benar-benar dapat merusak sesuatu dan bersifat transaksional, tidak sepertiALTER TYPE ... ADD
.default for column "my_column" cannot be cast automatically to type "my_enum"
. Anda harus melakukan yang berikut:ALTER TABLE "my_table" ALTER COLUMN "my_column" DROP DEFAULT, ALTER COLUMN "my_column" TYPE "my_type" USING ("my_column"::text::"my_type"), ALTER COLUMN "my_column" SET DEFAULT 'my_default_value';
Jika Anda jatuh ke dalam situasi ketika Anda harus menambahkan
enum
nilai dalam transaksi, maka jalankan dalam migrasi jalur terbang padaALTER TYPE
pernyataan Anda akan mendapatkan kesalahanERROR: ALTER TYPE ... ADD cannot run inside a transaction block
(lihat masalah jalur terbang # 350 ) Anda dapat menambahkan nilai-nilai tersebutpg_enum
secara langsung sebagai solusi (type_egais_units
adalah nama targetenum
):sumber
Melengkapi @Dariusz 1
Untuk Rails 4.2.1, ada bagian dokumen ini:
== Migrasi Transaksional
Jika adaptor database mendukung transaksi DDL, semua migrasi akan secara otomatis dibungkus dalam suatu transaksi. Ada pertanyaan yang tidak dapat Anda lakukan di dalam suatu transaksi, dan untuk situasi ini Anda dapat mematikan transaksi otomatis.
sumber
Dari Postgres 9.1 Dokumentasi :
Contoh:
sumber
Penafian: Saya belum mencoba solusi ini, jadi mungkin tidak berhasil ;-)
Anda harus melihat
pg_enum
. Jika Anda hanya ingin mengubah label ENUM yang ada, UPDATE sederhana akan melakukannya.Untuk menambahkan nilai ENUM baru:
pg_enum
. Jika nilai baru harus menjadi yang terakhir, Anda sudah selesai.pg_enum
urutan yang berlawanan.Ilustrasi
Anda memiliki serangkaian label berikut:
dan Anda ingin mendapatkan:
kemudian:
kemudian:
Dan seterusnya...
sumber
Sepertinya saya tidak bisa memposting komentar, jadi saya hanya akan mengatakan bahwa memperbarui pg_enum berfungsi di Postgres 8.4. Untuk cara enum kami diatur, saya telah menambahkan nilai baru ke tipe enum yang ada melalui:
Agak menakutkan, tetapi masuk akal mengingat cara Postgres menyimpan datanya.
sumber
Memperbarui pg_enum berfungsi, seperti halnya trik kolom perantara yang disorot di atas. Anda juga dapat menggunakan sihir USING untuk mengubah jenis kolom secara langsung:
Selama Anda tidak memiliki fungsi yang secara eksplisit membutuhkan atau mengembalikan enum itu, Anda baik. (pgsql akan mengeluh ketika Anda menjatuhkan tipenya jika ada.)
Juga, perhatikan bahwa PG9.1 memperkenalkan pernyataan ALTER TYPE, yang akan bekerja pada enum:
http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html
sumber
ALTER TABLE foo ALTER COLUMN bar TYPE test USING bar::text::new_type;
Tapi sekarang sebagian besar tidak relevan ...... USING bar::type
bekerja untuk saya. Saya bahkan tidak perlu menentukan::text
.Sederhana: singkirkan enum. Mereka tidak mudah dimodifikasi, dan karenanya sangat jarang digunakan.
sumber
Tidak dapat menambahkan komentar ke tempat yang sesuai, tetapi
ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type
gagal pada kolom default. Saya harus:ALTER table ALTER COLUMN bar DROP DEFAULT
;dan kemudian berhasil.
sumber
untuk berjaga-jaga, jika Anda menggunakan Rails dan Anda memiliki beberapa pernyataan, Anda perlu mengeksekusi satu per satu, seperti:
sumber
Berikut ini adalah solusi yang lebih umum tetapi lebih cepat bekerja, yang selain mengubah tipe itu sendiri memperbarui semua kolom dalam database yang menggunakannya. Metode ini dapat diterapkan bahkan jika versi baru ENUM berbeda dengan lebih dari satu label atau melewatkan beberapa yang asli. Kode di bawah ini diganti
my_schema.my_type AS ENUM ('a', 'b', 'c')
denganENUM ('a', 'b', 'd', 'e')
:Seluruh proses akan berjalan cukup cepat, karena jika urutan label tetap ada, tidak ada perubahan data aktual yang akan terjadi. Saya menerapkan metode pada 5 tabel menggunakan
my_type
dan memiliki 50.000−70.000 baris di masing-masing, dan seluruh proses hanya membutuhkan waktu 10 detik.Tentu saja, fungsi akan mengembalikan pengecualian jika label yang hilang dalam versi baru ENUM digunakan di suatu tempat dalam data, tetapi dalam situasi seperti itu, sesuatu harus dilakukan terlebih dahulu.
sumber
Bagi mereka yang mencari solusi dalam transaksi, berikut ini tampaknya berhasil.
Alih-alih a
ENUM
, aDOMAIN
harus digunakan pada tipeTEXT
dengan kendala yang memeriksa bahwa nilainya berada dalam daftar nilai-nilai yang diizinkan (seperti yang disarankan oleh beberapa komentar). Satu-satunya masalah adalah tidak ada kendala yang dapat ditambahkan (dan karenanya tidak dimodifikasi) ke domain jika digunakan oleh tipe komposit apa pun (dokumen hanya mengatakan ini "pada akhirnya harus ditingkatkan"). Pembatasan semacam itu dapat dilakukan, dengan menggunakan batasan memanggil fungsi, sebagai berikut.Sebelumnya, saya menggunakan solusi yang mirip dengan jawaban yang diterima, tetapi masih jauh dari baik sekali dilihat atau fungsi atau tipe komposit (dan terutama pandangan menggunakan pandangan lain menggunakan ENUM yang dimodifikasi ...) dianggap. Solusi yang diajukan dalam jawaban ini tampaknya berfungsi dalam kondisi apa pun.
Satu-satunya kelemahan adalah bahwa tidak ada pemeriksaan yang dilakukan pada data yang ada ketika beberapa nilai yang diizinkan dihapus (yang mungkin dapat diterima, terutama untuk pertanyaan ini). (Panggilan untuk
ALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check
mengakhiri dengan kesalahan yang sama seperti menambahkan kendala baru ke domain yang digunakan oleh tipe komposit, sayangnya.)Perhatikan bahwa sedikit modifikasi seperti(Berhasil, sebenarnya - itu adalah kesalahan saya)CHECK (value = ANY(get_allowed_values()))
, di managet_allowed_values()
fungsi mengembalikan daftar nilai yang diizinkan, tidak akan berfungsi - yang cukup aneh, jadi saya harap solusi yang diusulkan di atas berfungsi dengan andal (itu berlaku untuk saya, sejauh ini ...).sumber
Seperti dibahas di atas,
ALTER
perintah tidak dapat ditulis di dalam suatu transaksi. Cara yang disarankan adalah menyisipkan ke dalam tabel pg_enum secara langsung, olehretrieving the typelem from pg_type table
dancalculating the next enumsortorder number
;Berikut ini adalah kode yang saya gunakan. (Memeriksa apakah ada nilai duplikat sebelum memasukkan (batasan antara nama enumtypid dan enumlabel)
Perhatikan bahwa nama tipe Anda diawali dengan garis bawah pada tabel pg_type. Juga, nama samaran harus semuanya huruf kecil dalam klausa where.
Sekarang ini dapat ditulis dengan aman ke skrip migrasi db Anda.
sumber
Saya tidak tahu apakah memiliki opsi lain tetapi kami dapat menurunkan nilainya menggunakan:
sumber
Saat menggunakan Navicat Anda dapat pergi ke jenis (di bawah tampilan -> orang lain -> jenis) - dapatkan tampilan desain jenis - dan klik tombol "tambahkan label".
sumber
ERROR: cannot drop type foo because other objects depend on it HINT: Use DROP ... CASCADE to drop the dependent objects too.