जवाबों:
ध्यान दें कि यदि आप PostgreSQL 9.1 या बाद का उपयोग कर रहे हैं, और आप लेन-देन के बाहर परिवर्तन करने के साथ ठीक हैं, तो इस उत्तर को एक सरल दृष्टिकोण के लिए देखें ।
मुझे कुछ दिन पहले भी यही समस्या थी और मुझे यह पोस्ट मिली। तो मेरा उत्तर किसी ऐसे व्यक्ति के लिए मददगार हो सकता है जो समाधान ढूंढ रहा है :)
यदि आपके पास केवल एक या दो कॉलम हैं, तो आप जिस एनम प्रकार का उपयोग करना चाहते हैं, उसे बदलना चाहते हैं, तो आप यह कोशिश कर सकते हैं। इसके अलावा आप नए प्रकार में मूल्यों के क्रम को बदल सकते हैं।
-- 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;
1 से अधिक कॉलम होने पर 3-6 दोहराया जाना चाहिए।
ALTER TYPE
। लेकिन इससे पहले भी, ALTER TABLE foo ALTER COLUMN bar TYPE new_type USING bar::text::new_type;
बहुत बेहतर था।
ALTER TABLE some_table ALTER COLUMN some_column TYPE some_enum_type USING some_column::text::some_enum_type;
PostgreSQL 9.1 ALTER Enum प्रकारों की क्षमता का परिचय देता है :
ALTER TYPE enum_type ADD VALUE 'new_value'; -- appends to list
ALTER TYPE enum_type ADD VALUE 'new_value' BEFORE 'old_value';
ALTER TYPE enum_type ADD VALUE 'new_value' AFTER 'old_value';
एक संभावित समाधान निम्नलिखित है; पूर्व शर्त यह है, कि इस्तेमाल किए गए एनम मूल्यों में संघर्ष नहीं हैं। (उदाहरण के लिए जब कोई एनम मान निकालते हैं, तो सुनिश्चित करें कि यह मान अब उपयोग नहीं किया गया है।)
-- rename the old enum
alter type my_enum rename to my_enum__;
-- create the new enum
create type my_enum as enum ('value1', 'value2', 'value3');
-- alter all you enum columns
alter table my_table
alter column my_column type my_enum using my_column::text::my_enum;
-- drop the old enum
drop type my_enum__;
साथ ही इस तरह से कॉलम ऑर्डर नहीं बदला जाएगा।
pg_enum
वास्तव में चीजों को तोड़ सकते हैं और इसके विपरीत, लेन-देन कर सकते हैं ALTER TYPE ... ADD
।
default for column "my_column" cannot be cast automatically to type "my_enum"
। आपको निम्न कार्य करना होगा: 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';
यदि आप enum
लेन-देन में मूल्यों को जोड़ते समय स्थिति में आते हैं , तो फ़्लाई ALTER TYPE
स्टेटमेंट में इसे फ्लाईवे माइग्रेशन में निष्पादित करें। आपको त्रुटि मिलेगी ERROR: ALTER TYPE ... ADD cannot run inside a transaction block
( फ्लाईवे इश्यू # 350 देखें ) आप ऐसे मानों को pg_enum
सीधे वर्कअराउंड ( type_egais_units
लक्ष्य का नाम enum
) में जोड़ सकते हैं :
INSERT INTO pg_enum (enumtypid, enumlabel, enumsortorder)
SELECT 'type_egais_units'::regtype::oid, 'NEW_ENUM_VALUE', ( SELECT MAX(enumsortorder) + 1 FROM pg_enum WHERE enumtypid = 'type_egais_units'::regtype )
पूरक @ डारियस 1
रेल्स 4.2.1 के लिए, यह डॉक सेक्शन है:
== लेन-देन का माइग्रेशन
यदि डेटाबेस एडॉप्टर DDL लेनदेन का समर्थन करता है, तो सभी माइग्रेशन स्वचालित रूप से लेनदेन में लिपट जाएंगे। ऐसे प्रश्न हैं जो आप किसी लेनदेन के अंदर निष्पादित नहीं कर सकते हैं, और इन स्थितियों के लिए आप स्वचालित लेनदेन को बंद कर सकते हैं।
class ChangeEnum < ActiveRecord::Migration
disable_ddl_transaction!
def up
execute "ALTER TYPE model_size ADD VALUE 'new_value'"
end
end
Postgres से 9.1 प्रलेखन :
ALTER TYPE name ADD VALUE new_enum_value [ { BEFORE | AFTER } existing_enum_value ]
उदाहरण:
ALTER TYPE user_status ADD VALUE 'PROVISIONAL' AFTER 'NORMAL'
अस्वीकरण: मैंने इस समाधान की कोशिश नहीं की है, इसलिए यह काम नहीं कर सकता है ;-)
आपको देखना चाहिए pg_enum
। यदि आप केवल मौजूदा ENUM के लेबल को बदलना चाहते हैं, तो एक सरल अद्यतन करेगा।
एक नया ENUM मान जोड़ने के लिए:
pg_enum
। यदि नया मान अंतिम होना है, तो आप कर रहे हैं।pg_enum
विपरीत क्रम में उनका नाम बदलना होगा ।चित्रण
आपके पास लेबल का निम्नलिखित सेट है:
ENUM ('enum1', 'enum2', 'enum3')
और आप प्राप्त करना चाहते हैं:
ENUM ('enum1', 'enum1b', 'enum2', 'enum3')
फिर:
INSERT INTO pg_enum (OID, 'newenum3');
UPDATE TABLE SET enumvalue TO 'newenum3' WHERE enumvalue='enum3';
UPDATE TABLE SET enumvalue TO 'enum3' WHERE enumvalue='enum2';
फिर:
UPDATE TABLE pg_enum SET name='enum1b' WHERE name='enum2' AND enumtypid=OID;
और इसी तरह...
मैं एक टिप्पणी पोस्ट करने के लिए प्रतीत नहीं कर सकता, इसलिए मैं बस कहूंगा कि पोस्टग्रैस 8.4 में pg_enum काम करता है। जिस तरह से हमारे एनम की स्थापना की गई है, मैंने मौजूदा एनम प्रकारों के लिए नए मान जोड़े हैं:
INSERT INTO pg_enum (enumtypid, enumlabel)
SELECT typelem, 'NEWENUM' FROM pg_type WHERE
typname = '_ENUMNAME_WITH_LEADING_UNDERSCORE';
यह थोड़ा डरावना है, लेकिन यह समझ में आता है कि जिस तरह से Postgres वास्तव में अपने डेटा को स्टोर करता है।
अद्यतन pg_enum काम करता है, जैसा कि मध्यस्थ कॉलम चाल ऊपर प्रकाश डाला गया है। स्तंभ के प्रकार को सीधे बदलने के लिए एक USING जादू का उपयोग कर सकते हैं:
CREATE TYPE test AS enum('a', 'b');
CREATE TABLE foo (bar test);
INSERT INTO foo VALUES ('a'), ('b');
ALTER TABLE foo ALTER COLUMN bar TYPE varchar;
DROP TYPE test;
CREATE TYPE test as enum('a', 'b', 'c');
ALTER TABLE foo ALTER COLUMN bar TYPE test
USING CASE
WHEN bar = ANY (enum_range(null::test)::varchar[])
THEN bar::test
WHEN bar = ANY ('{convert, these, values}'::varchar[])
THEN 'c'::test
ELSE NULL
END;
जब तक आपके पास कोई कार्य नहीं है जो स्पष्ट रूप से आवश्यक है या उस एनम को वापस करना है, तो आप अच्छे हैं। (pgsql जब आप टाइप करेंगे तो शिकायत करेंगे।)
इसके अलावा, ध्यान दें कि PG9.1 ALTER TYPE स्टेटमेंट पेश कर रहा है, जो एनम पर काम करेगा:
http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html
ALTER TABLE foo ALTER COLUMN bar TYPE test USING bar::text::new_type;
लेकिन अब काफी हद तक अप्रासंगिक है ...
... USING bar::type
मेरे लिए काम किया। मुझे निर्दिष्ट करने की आवश्यकता नहीं थी ::text
।
सरलतम: enums से छुटकारा। वे आसानी से परिवर्तनीय नहीं हैं, और इस प्रकार बहुत कम उपयोग किया जाना चाहिए ।
उपयुक्त स्थान पर टिप्पणी नहीं जोड़ सकते, लेकिन ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type
स्तंभ पर डिफ़ॉल्ट के साथ विफल रहा। मुझे करना पड़ा:
ALTER table ALTER COLUMN bar DROP DEFAULT
;
और फिर इसने काम किया।
यहां एक अधिक सामान्य लेकिन एक तेजी से काम करने वाला समाधान है, जो स्वयं को बदलने के अलावा डेटाबेस का उपयोग करने वाले सभी स्तंभों को अपडेट करता है। विधि तब भी लागू की जा सकती है जब ENUM का एक नया संस्करण एक से अधिक लेबल से भिन्न हो या कुछ मूल को याद करता हो। की जगह नीचे कोड my_schema.my_type AS ENUM ('a', 'b', 'c')
के साथ ENUM ('a', 'b', 'd', 'e')
:
CREATE OR REPLACE FUNCTION tmp() RETURNS BOOLEAN AS
$BODY$
DECLARE
item RECORD;
BEGIN
-- 1. create new type in replacement to my_type
CREATE TYPE my_schema.my_type_NEW
AS ENUM ('a', 'b', 'd', 'e');
-- 2. select all columns in the db that have type my_type
FOR item IN
SELECT table_schema, table_name, column_name, udt_schema, udt_name
FROM information_schema.columns
WHERE
udt_schema = 'my_schema'
AND udt_name = 'my_type'
LOOP
-- 3. Change the type of every column using my_type to my_type_NEW
EXECUTE
' ALTER TABLE ' || item.table_schema || '.' || item.table_name
|| ' ALTER COLUMN ' || item.column_name
|| ' TYPE my_schema.my_type_NEW'
|| ' USING ' || item.column_name || '::text::my_schema.my_type_NEW;';
END LOOP;
-- 4. Delete an old version of the type
DROP TYPE my_schema.my_type;
-- 5. Remove _NEW suffix from the new type
ALTER TYPE my_schema.my_type_NEW
RENAME TO my_type;
RETURN true;
END
$BODY$
LANGUAGE 'plpgsql';
SELECT * FROM tmp();
DROP FUNCTION tmp();
पूरी प्रक्रिया काफी तेज़ी से चलेगी, क्योंकि यदि लेबल का क्रम जारी रहता है, तो डेटा का कोई वास्तविक परिवर्तन नहीं होगा। मैंने 5 तालिकाओं पर विधि का उपयोग किया my_type
और प्रत्येक में 50,000 the70,000 पंक्तियों का उपयोग किया , और पूरी प्रक्रिया में सिर्फ 10 सेकंड लगे।
बेशक, फ़ंक्शन मामले में एक अपवाद लौटाएगा यदि ENUM के नए संस्करण में गायब होने वाले लेबल डेटा में कहीं उपयोग किए जाते हैं, लेकिन ऐसी स्थिति में कुछ भी पहले से किया जाना चाहिए।
लेन-देन समाधान की तलाश करने वालों के लिए, निम्नलिखित काम करने लगता है।
इसके बजाय ENUM
, एक DOMAIN
प्रकार का उपयोग TEXT
एक बाधा की जाँच के साथ किया जाएगा कि मान अनुमत मानों की निर्दिष्ट सूची में है (जैसा कि कुछ टिप्पणियों द्वारा सुझाया गया है)। एकमात्र समस्या यह है कि किसी भी प्रकार के अवरोध को किसी डोमेन में नहीं जोड़ा जा सकता है (और इस प्रकार न तो संशोधित किया जाता है) यदि इसका उपयोग किसी भी समग्र प्रकार से किया जाता है (डॉक्स केवल यह कहता है कि "अंततः सुधार किया जाना चाहिए")। इस तरह के प्रतिबंध के चारों ओर काम किया जा सकता है, हालांकि, एक समारोह का आह्वान करते हुए, इस प्रकार है।
START TRANSACTION;
CREATE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
SELECT lbl IN ('one', 'two', 'three');
$function$ LANGUAGE SQL IMMUTABLE;
CREATE DOMAIN test_domain AS TEXT CONSTRAINT val_check CHECK (test_is_allowed_label(value));
CREATE TYPE test_composite AS (num INT, word test_domain);
CREATE TABLE test_table (val test_composite);
INSERT INTO test_table (val) VALUES ((1, 'one')::test_composite), ((3, 'three')::test_composite);
-- INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- restricted by the CHECK constraint
CREATE VIEW test_view AS SELECT * FROM test_table; -- just to show that the views using the type work as expected
CREATE OR REPLACE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
SELECT lbl IN ('one', 'two', 'three', 'four');
$function$ LANGUAGE SQL IMMUTABLE;
INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- allowed by the new effective definition of the constraint
SELECT * FROM test_view;
CREATE OR REPLACE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
SELECT lbl IN ('one', 'two', 'three');
$function$ LANGUAGE SQL IMMUTABLE;
-- INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- restricted by the CHECK constraint, again
SELECT * FROM test_view; -- note the view lists the restricted value 'four' as no checks are made on existing data
DROP VIEW test_view;
DROP TABLE test_table;
DROP TYPE test_composite;
DROP DOMAIN test_domain;
DROP FUNCTION test_is_allowed_label(TEXT);
COMMIT;
पहले, मैंने स्वीकार किए गए उत्तर के समान एक समाधान का उपयोग किया था, लेकिन यह एक बार के विचारों या कार्यों या समग्र प्रकारों (और विशेष रूप से संशोधित ENUMs का उपयोग करके अन्य विचारों का उपयोग करने वाले विचारों से अच्छा है) से दूर माना जाता है। इस उत्तर में प्रस्तावित समाधान किसी भी परिस्थिति में काम करता है।
एकमात्र नुकसान यह है कि कुछ अनुमत मूल्यों को हटाए जाने पर मौजूदा डेटा पर कोई चेक नहीं किया जाता है (जो स्वीकार्य हो सकता है, खासकर इस प्रश्न के लिए)। (ए कॉल टूALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check
दुर्भाग्यवश, समग्र रूप से उपयोग किए गए डोमेन में एक नया अवरोध जोड़ने के रूप में एक ही त्रुटि के साथ समाप्त होता है।)
ध्यान दें कि इस तरह के एक मामूली संशोधन (यह काम करता है, वास्तव में - यह मेरी त्रुटि थी)CHECK (value = ANY(get_allowed_values()))
, जहां get_allowed_values()
फ़ंक्शन ने अनुमत मानों की सूची लौटा दी है, काम नहीं करेगा - जो काफी अजीब है, इसलिए मुझे उम्मीद है कि ऊपर प्रस्तावित समाधान मज़बूती से काम करता है (यह मेरे लिए, अब तक ...)।
जैसा कि ऊपर चर्चा की गई है, ALTER
एक लेनदेन के अंदर कमांड नहीं लिखा जा सकता है। सुझाया गया तरीका सीधे retrieving the typelem from pg_type table
और इसके द्वारा pg_enum तालिका में सम्मिलित करना हैcalculating the next enumsortorder number
;
निम्नलिखित कोड है जो मैं उपयोग करता हूं। (जाँचता है कि डालने से पहले डुप्लिकेट मान मौजूद है (एन्युमेटिपिड और एनुमबेल नाम के बीच अवरोध)
INSERT INTO pg_enum (enumtypid, enumlabel, enumsortorder)
SELECT typelem,
'NEW_ENUM_VALUE',
(SELECT MAX(enumsortorder) + 1
FROM pg_enum e
JOIN pg_type p
ON p.typelem = e.enumtypid
WHERE p.typname = '_mytypename'
)
FROM pg_type p
WHERE p.typname = '_mytypename'
AND NOT EXISTS (
SELECT * FROM
pg_enum e
JOIN pg_type p
ON p.typelem = e.enumtypid
WHERE e.enumlabel = 'NEW_ENUM_VALUE'
AND p.typname = '_mytypename'
)
ध्यान दें कि आपका प्रकार नाम pg_type तालिका में अंडरस्कोर के साथ पहले से लिखा हुआ है। इसके अलावा, टाइपनाम को क्लॉज में सभी लोअरकेस में होना चाहिए।
अब यह आपके db माइग्रेट स्क्रिप्ट में सुरक्षित रूप से लिखा जा सकता है।
मुझे नहीं पता कि क्या अन्य विकल्प है लेकिन हम मूल्य का उपयोग कर छोड़ सकते हैं:
select oid from pg_type where typname = 'fase';'
select * from pg_enum where enumtypid = 24773;'
select * from pg_enum where enumtypid = 24773 and enumsortorder = 6;
delete from pg_enum where enumtypid = 24773 and enumsortorder = 6;
Navicat का उपयोग करते समय आप टाइप (देखने के लिए -> अन्य -> प्रकार) पर जा सकते हैं - टाइप का डिज़ाइन दृश्य प्राप्त करें - और "लेबल जोड़ें" बटन पर क्लिक करें।
ERROR: cannot drop type foo because other objects depend on it HINT: Use DROP ... CASCADE to drop the dependent objects too.