HINWEIS Wenn Sie PostgreSQL 9.1 oder höher verwenden und Änderungen außerhalb einer Transaktion vornehmen können, finden Sie in dieser Antwort einen einfacheren Ansatz.
Ich hatte vor ein paar Tagen das gleiche Problem und fand diesen Beitrag. Meine Antwort kann also für jemanden hilfreich sein, der nach einer Lösung sucht :)
Wenn Sie nur eine oder zwei Spalten haben, die den Aufzählungstyp verwenden, den Sie ändern möchten, können Sie dies versuchen. Sie können auch die Reihenfolge der Werte im neuen Typ ändern.
-- 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 sollte wiederholt werden, wenn mehr als eine Spalte vorhanden ist.
ALTER TYPE
. Aber schon vorherALTER TABLE foo ALTER COLUMN bar TYPE new_type USING bar::text::new_type;
war weit überlegen.ALTER TABLE some_table ALTER COLUMN some_column TYPE some_enum_type USING some_column::text::some_enum_type;
PostgreSQL 9.1 bietet die Möglichkeit, die Aufzählungstypen zu ändern:
quelle
Eine mögliche Lösung ist die folgende; Voraussetzung ist, dass es keine Konflikte in den verwendeten Enum-Werten gibt. (Stellen Sie z. B. beim Entfernen eines Aufzählungswerts sicher, dass dieser Wert nicht mehr verwendet wird.)
Auch auf diese Weise wird die Spaltenreihenfolge nicht geändert.
quelle
pg_enum
, die Dinge kaputt machen können und im Gegensatz zu TransaktionenALTER TYPE ... ADD
.default for column "my_column" cannot be cast automatically to type "my_enum"
. Sie müssen Folgendes tun: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';
Wenn Sie in eine Situation geraten, in der Sie
enum
Werte in eine Transaktion einfügen sollten, z. B. wenn Sie diese in der Flyway-Migration ausführen, erhalten SieALTER TYPE
eine FehlermeldungERROR: ALTER TYPE ... ADD cannot run inside a transaction block
(siehe Flyway-Problem Nr. 350 ). Sie können solche Wertepg_enum
direkt als Problemumgehung hinzufügen (type_egais_units
ist der Name des Zielsenum
):quelle
Ergänzung zu @Dariusz 1
Für Rails 4.2.1 gibt es diesen Dokumentabschnitt:
== Transaktionsmigrationen
Wenn der Datenbankadapter DDL-Transaktionen unterstützt, werden alle Migrationen automatisch in eine Transaktion eingeschlossen. Es gibt Abfragen, die Sie innerhalb einer Transaktion nicht ausführen können, und in diesen Situationen können Sie die automatischen Transaktionen deaktivieren.
quelle
Aus Postgres 9.1 Dokumentation :
Beispiel:
quelle
Haftungsausschluss: Ich habe diese Lösung nicht ausprobiert, daher funktioniert sie möglicherweise nicht ;-)
Sie sollten sich ansehen
pg_enum
. Wenn Sie nur die Bezeichnung einer vorhandenen ENUM ändern möchten, wird dies durch ein einfaches UPDATE erledigt.So fügen Sie neue ENUM-Werte hinzu:
pg_enum
. Wenn der neue Wert der letzte sein muss, sind Sie fertig.pg_enum
in umgekehrter Reihenfolge umbenennen .Illustration
Sie haben die folgenden Beschriftungen:
und Sie möchten erhalten:
dann:
dann:
Und so weiter...
quelle
Ich kann anscheinend keinen Kommentar veröffentlichen, daher sage ich nur, dass das Aktualisieren von pg_enum in Postgres 8.4 funktioniert. Für die Einrichtung unserer Aufzählungen habe ich vorhandenen Aufzählungstypen neue Werte hinzugefügt über:
Es ist ein wenig beängstigend, aber angesichts der Art und Weise, wie Postgres seine Daten tatsächlich speichert, ist es sinnvoll.
quelle
Das Aktualisieren von pg_enum funktioniert ebenso wie der oben hervorgehobene Trick der Zwischenspalte. Sie können auch USING magic verwenden, um den Spaltentyp direkt zu ändern:
Solange Sie keine Funktionen haben, die diese Aufzählung explizit erfordern oder zurückgeben, sind Sie gut. (pgsql wird sich beschweren, wenn Sie den Typ löschen, falls vorhanden.)
Beachten Sie außerdem, dass PG9.1 eine ALTER TYPE-Anweisung einführt, die für Aufzählungen funktioniert:
http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html
quelle
ALTER TABLE foo ALTER COLUMN bar TYPE test USING bar::text::new_type;
Aber jetzt weitgehend irrelevant ...... USING bar::type
arbeitete für mich. Ich musste nicht einmal angeben::text
.Am einfachsten: Aufzählungen loswerden. Sie sind nicht leicht zu modifizieren und sollten daher sehr selten verwendet werden.
quelle
Es kann kein Kommentar an der entsprechenden Stelle hinzugefügt werden, aber
ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type
ein Standardwert für die Spalte ist fehlgeschlagen. Ich musste:ALTER table ALTER COLUMN bar DROP DEFAULT
;;und dann hat es funktioniert.
quelle
Nur für den Fall, dass Sie Rails verwenden und mehrere Anweisungen haben, müssen Sie eine nach der anderen ausführen, wie zum Beispiel:
quelle
Hier ist eine allgemeinere, aber ziemlich schnell arbeitende Lösung, die neben der Änderung des Typs selbst alle Spalten in der Datenbank aktualisiert, die sie verwenden. Die Methode kann auch angewendet werden, wenn sich eine neue Version von ENUM um mehr als eine Bezeichnung unterscheidet oder einige der ursprünglichen fehlen. Der folgende Code ersetzt
my_schema.my_type AS ENUM ('a', 'b', 'c')
durchENUM ('a', 'b', 'd', 'e')
:Der gesamte Prozess wird ziemlich schnell ausgeführt, da bei Beibehaltung der Reihenfolge der Beschriftungen keine tatsächliche Änderung der Daten erfolgt. Ich habe die Methode auf 5 Tabellen mit angewendet
my_type
50.000 bis 70.000 Zeilen , und der gesamte Vorgang dauerte nur 10 Sekunden.Natürlich gibt die Funktion eine Ausnahme zurück, falls Beschriftungen, die in der neuen Version von ENUM fehlen, irgendwo in den Daten verwendet werden, aber in einer solchen Situation sollte sowieso vorher etwas getan werden.
quelle
Für diejenigen, die nach einer In-Transaction-Lösung suchen, scheint Folgendes zu funktionieren.
Anstelle von
ENUM
aDOMAIN
soll a für einen TypTEXT
mit einer Einschränkung verwendet werden, die überprüft, ob der Wert innerhalb der angegebenen Liste der zulässigen Werte liegt (wie in einigen Kommentaren vorgeschlagen). Das einzige Problem besteht darin, dass einer Domäne keine Einschränkung hinzugefügt (und somit auch nicht geändert) werden kann, wenn sie von einem zusammengesetzten Typ verwendet wird (die Dokumentation sagt lediglich, dass dies "eventuell verbessert werden sollte"). Eine solche Einschränkung kann jedoch unter Verwendung einer Einschränkung, die eine Funktion aufruft, wie folgt umgangen werden.Früher habe ich eine Lösung verwendet, die der akzeptierten Antwort ähnelt, aber es ist alles andere als gut, wenn Ansichten oder Funktionen oder zusammengesetzte Typen (und insbesondere Ansichten, die andere Ansichten mit den geänderten ENUMs verwenden ...) berücksichtigt werden. Die in dieser Antwort vorgeschlagene Lösung scheint unter allen Bedingungen zu funktionieren.
Der einzige Nachteil ist, dass keine Überprüfungen vorhandener Daten durchgeführt werden, wenn einige zulässige Werte entfernt werden (was insbesondere für diese Frage akzeptabel sein kann). (Ein Aufruf von
ALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check
endet am Ende mit demselben Fehler wie das Hinzufügen einer neuen Einschränkung zu der Domäne, die leider von einem zusammengesetzten Typ verwendet wird.)Beachten Sie, dass eine geringfügige Änderung, z. B.(es funktioniert tatsächlich - es war mein Fehler)CHECK (value = ANY(get_allowed_values()))
wenn dieget_allowed_values()
Funktion die Liste der zulässigen Werte zurückgibt, nicht funktioniert - was ziemlich seltsam ist. Ich hoffe, dass die oben vorgeschlagene Lösung zuverlässig funktioniert (für mich bisher ...).quelle
Wie oben erläutert,
ALTER
kann der Befehl nicht in eine Transaktion geschrieben werden. Der vorgeschlagene Weg besteht darin, direkt, durchretrieving the typelem from pg_type table
und in die Tabelle pg_enum einzufügencalculating the next enumsortorder number
.Es folgt der Code, den ich verwende. (Überprüft vor dem Einfügen, ob ein doppelter Wert vorhanden ist (Einschränkung zwischen enumtypid und enumlabel name)
Beachten Sie, dass Ihrem Typnamen in der Tabelle pg_type ein Unterstrich vorangestellt ist. Außerdem muss der Typname in der where-Klausel nur in Kleinbuchstaben angegeben werden.
Jetzt kann dies sicher in Ihr DB-Migrationsskript geschrieben werden.
quelle
Ich weiß nicht, ob ich eine andere Option habe, aber wir können den Wert löschen mit:
quelle
Wenn Sie Navicat verwenden, können Sie zu Typen gehen (unter Ansicht -> Andere -> Typen) - die Entwurfsansicht des Typs abrufen - und auf die Schaltfläche "Beschriftung hinzufügen" klicken.
quelle
ERROR: cannot drop type foo because other objects depend on it HINT: Use DROP ... CASCADE to drop the dependent objects too.