Postgresql - Ändern Sie die Größe einer Varchar-Spalte auf eine geringere Länge

153

Ich habe eine Frage zum ALTER TABLEBefehl in einer wirklich großen Tabelle (fast 30 Millionen Zeilen). Eine seiner Spalten ist a varchar(255)und ich möchte die Größe auf a ändern varchar(40). Grundsätzlich möchte ich meine Spalte ändern, indem ich den folgenden Befehl ausführe:

ALTER TABLE mytable ALTER COLUMN mycolumn TYPE varchar(40);

Ich habe kein Problem, wenn der Prozess sehr lang ist, aber es scheint, dass meine Tabelle während des Befehls ALTER TABLE nicht mehr lesbar ist. Gibt es einen klügeren Weg? Vielleicht eine neue Spalte hinzufügen, Werte aus der alten Spalte kopieren, die alte Spalte löschen und schließlich die neue umbenennen?

Jeder Hinweis wird sehr geschätzt! Danke im Voraus,

Hinweis: Ich verwende PostgreSQL 9.0.

Labynocle
quelle
11
resizingUm ganz klar zu sein: Wissen Sie, dass der Tisch dadurch nicht weniger Platz einnimmt?
AH
Selbst in meinem Fall? Ich meine, die Spalte hat eine maximale Größe von 40 Zeichen (also Oktette) anstelle von 255?
Labynocle
16
Wenn Sie varchar(255)zu PostgreSQL sagen , werden 255 Bytes für einen Wert mit einer tatsächlichen Länge von 40 Bytes nicht zugewiesen. Es werden 40 Bytes (plus interner Overhead) zugewiesen. Das einzige, was be changed by the ALTER TABLE` ist, ist die maximale Anzahl von Bytes, die Sie in dieser Spalte speichern können, ohne einen Fehler von PG zu erhalten.
AH
Über den Overhead AH erwähnt: Was ist der Overhead für varchar (n)?
Erwin Brandstetter
Überprüfen Sie die Antwort hier für ein Update dba.stackexchange.com/questions/189890/…
Evan Carroll

Antworten:

73

Eine Beschreibung dazu finden Sie unter Ändern der Größe einer Spalte in einer PostgreSQL-Tabelle, ohne Daten zu ändern . Sie müssen die Datenbankkatalogdaten hacken. Die einzige Möglichkeit, dies offiziell zu tun, ist mit ALTER TABLE. Wie Sie bereits bemerkt haben, wird durch die Änderung die gesamte Tabelle gesperrt und neu geschrieben, während sie ausgeführt wird.

Stellen Sie sicher, dass Sie den Abschnitt Zeichentypen in den Dokumenten gelesen haben, bevor Sie dies ändern. Alle möglichen seltsamen Fälle, die hier zu beachten sind. Die Längenprüfung wird durchgeführt, wenn Werte in den Zeilen gespeichert werden. Wenn Sie dort eine Untergrenze hacken, wird die Größe der vorhandenen Werte überhaupt nicht verringert. Es ist ratsam, die gesamte Tabelle nach Zeilen zu durchsuchen, deren Feldlänge nach der Änderung> 40 Zeichen beträgt. Sie müssen herausfinden, wie Sie diese manuell abschneiden können - damit Sie einige Sperren nur bei übergroßen zurücksetzen -, denn wenn jemand versucht, etwas in dieser Zeile zu aktualisieren, wird es jetzt als zu groß zurückgewiesen Es wird die neue Version der Zeile gespeichert. Heiterkeit entsteht für den Benutzer.

VARCHAR ist ein schrecklicher Typ, der in PostgreSQL nur existiert, um den damit verbundenen schrecklichen Teil des SQL-Standards zu erfüllen. Wenn Sie sich nicht für die Kompatibilität mit mehreren Datenbanken interessieren, sollten Sie Ihre Daten als TEXT speichern und eine Einschränkung hinzufügen, um ihre Länge zu begrenzen. Einschränkungen, die Sie ohne dieses Problem beim Sperren / Umschreiben von Tabellen ändern können, können mehr Integritätsprüfungen durchführen als nur die Prüfung der schwachen Länge.

Greg Smith
quelle
Danke für die Antwort. Ich werde Ihren Link überprüfen. Ich mache mir keine Sorgen um die manuelle Größenprüfung, da alle meine Inhalte eine maximale Größe von 40 Zeichen haben. Ich muss mehr über Einschränkungen für TEXT lesen, weil ich glaubte, dass VARCHAR besser ist, um die Lentgh zu überprüfen :)
Labynocle
6
Durch Ändern der Varchar-Länge wird die Tabelle nicht neu geschrieben. Es wird lediglich die Einschränkungslänge für die gesamte Tabelle genau als CHECK CONSTRAINT überprüft. Wenn Sie die Länge erhöhen, gibt es nichts zu tun. Nur das nächste Einfügen oder Aktualisieren akzeptiert eine größere Länge. Wenn Sie die Länge verringern und alle Zeilen die neue kleinere Einschränkung erfüllen, ergreift Pg keine weiteren Maßnahmen, damit die nächsten Einfügungen oder Aktualisierungen nur die neue Länge schreiben können.
Maniero
3
@bigown, nur um zu verdeutlichen, Ihre Aussage gilt nur für PostgreSQL 9.2+ , nicht für die alten.
MatheusOl
12
Der Link ist jetzt tot.
Raarts
Weitere Informationen dazu finden Sie unter dba.stackexchange.com/questions/189890/…
Evan Carroll
100

In PostgreSQL 9.1 gibt es einen einfacheren Weg

http://www.postgresql.org/message-id/[email protected]

CREATE TABLE foog(a varchar(10));

ALTER TABLE foog ALTER COLUMN a TYPE varchar(30);

postgres=# \d foog

 Table "public.foog"
 Column |         Type          | Modifiers
--------+-----------------------+-----------
 a      | character varying(30) |
sir_leslie
quelle
6
Beachten Sie, dass dies nur funktioniert, weil Sie eine größere Größe angeben (30> 10). Wenn die Größe kleiner ist, erhalten Sie den gleichen Fehler wie ich .
Matthieu
2
Postgres sollte keinen Fehler auslösen, wenn Sie die Varchar-Größe über eine ALTER TABLE-Abfrage verringern, es sei denn, eine der mehreren Zeilen enthält einen Wert, der die neue Größe überschreitet.
Sagen Sie
@Tell, interessant. Bedeutet das, dass Postgres die Tabelle vollständig scannt oder die maximale Größe in der Statistik beibehält?
Matthieu
47

Ok, ich bin wahrscheinlich zu spät zur Party, ABER ...

Es besteht keine Notwendigkeit, die Größe der Spalte in Ihrem Fall zu ändern!

Postgres ist im Gegensatz zu einigen anderen Datenbanken intelligent genug, um nur genügend Speicherplatz für die Zeichenfolge zu verwenden (auch bei längerer Zeichenfolge wird die Komprimierung verwendet). Selbst wenn Ihre Spalte als VARCHAR (255) deklariert ist - wenn Sie Zeichenfolgen mit 40 Zeichen in speichern In der Spalte beträgt die Speicherplatznutzung 40 Byte + 1 Byte Overhead.

Die Speicheranforderung für eine kurze Zeichenfolge (bis zu 126 Byte) beträgt 1 Byte plus der tatsächlichen Zeichenfolge, einschließlich der Leerzeichenauffüllung im Fall von Zeichen. Längere Zeichenfolgen haben 4 Byte Overhead anstelle von 1. Lange Zeichenfolgen werden vom System automatisch komprimiert, sodass die physischen Anforderungen auf der Festplatte möglicherweise geringer sind. Sehr lange Werte werden auch in Hintergrundtabellen gespeichert, damit sie den schnellen Zugriff auf kürzere Spaltenwerte nicht beeinträchtigen.

( http://www.postgresql.org/docs/9.0/interactive/datatype-character.html )

Die Größenangabe in VARCHAR wird nur verwendet, um die Größe der eingefügten Werte zu überprüfen. Sie hat keinen Einfluss auf das Festplattenlayout. Tatsächlich werden VARCHAR- und TEXT-Felder in Postgres auf dieselbe Weise gespeichert .

Sergey
quelle
8
Nie zu spät, um weitere Informationen zum "Warum" hinzuzufügen! Vielen Dank für all diese Informationen
Labynocle
Manchmal müssen Sie in der Struktur Ihrer Datenbank konsistent sein. Auch wenn 2 Spalten keine Beziehung haben, können sie aus konzeptioneller Sicht eine Beziehung haben, z. B. das Modell EAV auschecken.
Alexandre
36

Ich hatte das gleiche Problem beim Versuch, ein VARCHAR von 32 auf 8 zu kürzen und das zu bekommen ERROR: value too long for type character varying(8). Ich möchte so nah wie möglich an SQL bleiben, da ich eine selbst erstellte JPA-ähnliche Struktur verwende, die wir je nach Wahl des Kunden möglicherweise auf ein anderes DBMS umstellen müssen (PostgreSQL ist die Standardstruktur). Daher möchte ich nicht den Trick verwenden, Systemtabellen zu ändern.

Ich beendete die Verwendung der USINGAnweisung in ALTER TABLE:

ALTER TABLE "MY_TABLE" ALTER COLUMN "MyColumn" TYPE varchar(8)
USING substr("MyColumn", 1, 8)

Wie @raylu bemerkte, ALTERerhält der Tisch eine exklusive Sperre, sodass alle anderen Vorgänge verzögert werden, bis sie abgeschlossen sind.

Matthieu
quelle
2
Der ALTERerwirbt eine exklusive Sperre auf dem Tisch und verhindert alle anderen Operationen
Raylu
8

Das Hinzufügen einer neuen Spalte und das Ersetzen einer neuen durch eine alte Spalte, die für mich funktioniert hat, finden Sie unter redshift postgresql unter diesem Link unter https://gist.github.com/mmasashi/7107430

BEGIN;
LOCK users;
ALTER TABLE users ADD COLUMN name_new varchar(512) DEFAULT NULL;
UPDATE users SET name_new = name;
ALTER TABLE users DROP name;
ALTER TABLE users RENAME name_new TO name;
END;
Gamaschen
quelle
7

Hier ist der Cache der von Greg Smith beschriebenen Seite. Falls dies ebenfalls stirbt, sieht die alter-Anweisung folgendermaßen aus:

UPDATE pg_attribute SET atttypmod = 35+4
WHERE attrelid = 'TABLE1'::regclass
AND attname = 'COL1';

Wenn Ihre Tabelle TABLE1 ist, ist die Spalte COL1 und Sie möchten sie auf 35 Zeichen setzen (die +4 werden für Legacy-Zwecke gemäß dem Link benötigt, möglicherweise der Overhead, auf den AH in den Kommentaren verweist).

Tom
quelle
7

Wenn Sie die Änderung in eine Transaktion einfügen, sollte die Tabelle nicht gesperrt werden:

BEGIN;
  ALTER TABLE "public"."mytable" ALTER COLUMN "mycolumn" TYPE varchar(40);
COMMIT;

Das funktionierte für mich blitzschnell, einige Sekunden auf einem Tisch mit mehr als 400.000 Zeilen.

Jacktrade
quelle
5
Warum sollte der explizite Transaktions-Wrapper das Sperrverhalten der ALTERAnweisung ändern ? Das tut es nicht.
Erwin Brandstetter
Probieren Sie es aus, mit und ohne Transaktions-Wrapper werden Sie einen großen Unterschied feststellen.
Jacktrade
2
Ihre Antwort ist grundsätzlich falsch. Jede DDL-Anweisung ohne expliziten Transaktionswrapper wird implizit in einer Transaktion ausgeführt. Der einzig mögliche Effekt der expliziten Transaktion besteht darin, dass Sperren länger beibehalten werden - bis zur expliziten COMMIT. Der Wrapper ist nur dann sinnvoll, wenn Sie mehr Befehle in dieselbe Transaktion einfügen möchten.
Erwin Brandstetter
Sie haben vollkommen recht, aber ich bestehe darauf: Versuchen Sie es selbst, fahren Sie fort. und dann fragen, warum nicht auf die gleiche Weise funktioniert.
Jacktrade
Hat bei Postgres 9.3 nicht geholfen.
Noumenon
1

Ich habe eine sehr einfache Möglichkeit gefunden, die Größe zu ändern, dh die Anmerkung @Size (min = 1, max = 50), die Teil von "import javax.validation.constraints" ist, dh "import javax.validation.constraints.Size;"

@Size(min = 1, max = 50)
private String country;


when executing  this is hibernate you get in pgAdmin III 


CREATE TABLE address
(
.....
  country character varying(50),

.....

)
Tito
quelle
Danke für deinen Beitrag! Bitte verwenden Sie keine Signaturen / Slogans in Ihren Posts. Ihre Benutzerbox zählt als Ihre Signatur, und Sie können Ihr Profil verwenden, um Informationen über sich selbst zu veröffentlichen, die Sie mögen. FAQ zu Unterschriften / Slogans
Andrew Barber
0

Führen Sie die folgende Änderungstabelle aus:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
Никита Верёвкин
quelle