Postgresql: cambie el tamaño de una columna varchar a menor longitud

153

Tengo una pregunta sobre el ALTER TABLEcomando en una tabla realmente grande (casi 30 millones de filas). Una de sus columnas es a varchar(255)y me gustaría cambiar su tamaño a a varchar(40). Básicamente, me gustaría cambiar mi columna ejecutando el siguiente comando:

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

No tengo ningún problema si el proceso es muy largo, pero parece que mi tabla ya no es legible durante el comando ALTER TABLE. ¿Hay una manera más inteligente? ¿Quizás agregar una nueva columna, copiar valores de la columna anterior, soltar la columna antigua y finalmente cambiar el nombre de la nueva?

Cualquier pista será muy apreciada! Gracias por adelantado,

Nota: Yo uso PostgreSQL 9.0.

Laberinto
fuente
11
Para que quede claro: ¿sabes que resizingeso no hará que la mesa ocupe menos espacio?
AH
incluso en mi caso, ¿quiero decir que la columna tendrá un tamaño máximo de 40 caracteres (por lo tanto, octetos) en lugar de 255?
Labynocle
16
Si le dice varchar(255)a PostgreSQL, entonces no asignará 255 bytes para un valor cuya longitud real es 40 bytes. Asignará 40 bytes (más algunos gastos generales internos). Lo único que be changed by the ALTERARÁ LA TABLA` es el número máximo de bytes que puede almacenar en esa columna sin que PG obtenga un error.
AH
Sobre la sobrecarga AH mencionada: ¿Cuál es la sobrecarga para varchar (n)?
Erwin Brandstetter
Vea la respuesta aquí para obtener una actualización dba.stackexchange.com/questions/189890/…
Evan Carroll

Respuestas:

73

Hay una descripción de cómo hacer esto en Cambiar el tamaño de una columna en una tabla PostgreSQL sin cambiar los datos . Tienes que hackear los datos del catálogo de la base de datos. La única forma de hacerlo oficialmente es ALTER TABLE, y como has notado, el cambio bloqueará y reescribirá toda la tabla mientras se ejecuta.

Asegúrese de leer la sección Tipos de caracteres de los documentos antes de cambiar esto. Todo tipo de casos extraños a tener en cuenta aquí. La verificación de longitud se realiza cuando los valores se almacenan en las filas. Si piratea un límite inferior allí, eso no reducirá en absoluto el tamaño de los valores existentes. Sería aconsejable hacer un análisis de toda la tabla en busca de filas donde la longitud del campo es> 40 caracteres después de realizar el cambio. Tendrá que encontrar la forma de truncarlos manualmente, para que pueda recuperar algunos bloqueos solo en los de gran tamaño, porque si alguien intenta actualizar algo en esa fila, lo rechazará como demasiado grande ahora, en ese momento se va a almacenar la nueva versión de la fila. Se produce hilaridad para el usuario.

VARCHAR es un tipo terrible que existe en PostgreSQL solo para cumplir con su parte terrible asociada del estándar SQL. Si no le importa la compatibilidad con múltiples bases de datos, considere almacenar sus datos como TEXTO y agregue una restricción para limitar su longitud. Las restricciones que puede cambiar sin este problema de bloqueo / reescritura de la tabla, y pueden hacer más verificaciones de integridad que solo la verificación de longitud débil.

Greg Smith
fuente
Gracias por la respuesta. Comprobaré tu enlace. No me preocupa la verificación manual del tamaño porque todo mi contenido tiene un tamaño máximo de 40 caracteres. Necesito leer más sobre las restricciones en TEXT porque creía que VARCHAR era mejor para verificar la longitud :)
Labynocle
66
Cambiar la longitud de varchar no reescribe la tabla. Simplemente verifica la longitud de la restricción contra toda la tabla exactamente como CHECK CONSTRAINT. Si aumenta la longitud, no hay nada que hacer, solo la próxima inserción o las actualizaciones aceptarán una longitud mayor. Si disminuye la longitud y todas las filas pasan la nueva restricción más pequeña, Pg no toma ninguna otra acción además de permitir que las próximas inserciones o actualizaciones escriban solo la nueva longitud.
Maniero
3
@bigown, solo para aclarar, su declaración solo es cierta para PostgreSQL 9.2+ , no para las antiguas.
MatheusOl
12
El enlace ahora está muerto.
raarts
Para obtener más información sobre cómo funciona, visite dba.stackexchange.com/questions/189890/…
Evan Carroll
100

En PostgreSQL 9.1 hay una manera más fácil

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
fuente
66
Tenga en cuenta que solo funciona porque está especificando un tamaño más grande (30> 10). Si el tamaño es más pequeño, obtendrá el mismo error que yo .
Matthieu
2
Postgres no debería arrojar un error si reduce el tamaño de varchar a través de una consulta ALTER TABLE a menos que una o más filas contenga un valor que exceda el nuevo tamaño.
Dile el
@Tell, interesante. ¿Eso significa que Postgres realiza un análisis completo de la tabla o de alguna manera mantiene el tamaño máximo en sus estadísticas?
Matthieu
47

Ok, probablemente llegue tarde a la fiesta, PERO ...

¡NO HAY NECESIDAD DE REDimensionAR LA COLUMNA EN SU CASO!

Postgres, a diferencia de otras bases de datos, es lo suficientemente inteligente como para usar solo el espacio suficiente para acomodar la cadena (incluso usando compresión para cadenas más largas), por lo que incluso si su columna se declara como VARCHAR (255), si almacena cadenas de 40 caracteres en la columna, el uso del espacio será de 40 bytes + 1 byte de sobrecarga.

El requisito de almacenamiento para una cadena corta (hasta 126 bytes) es de 1 byte más la cadena real, que incluye el relleno de espacio en el caso de caracteres. Las cadenas más largas tienen 4 bytes de sobrecarga en lugar de 1. Las cadenas largas son comprimidas por el sistema automáticamente, por lo que el requerimiento físico en el disco podría ser menor. Los valores muy largos también se almacenan en tablas de fondo para que no interfieran con el acceso rápido a valores de columna más cortos.

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

La especificación de tamaño en VARCHAR solo se usa para verificar el tamaño de los valores que se insertan, no afecta el diseño del disco. De hecho, los campos VARCHAR y TEXT se almacenan de la misma manera en Postgres .

Sergey
fuente
8
¡Nunca es tarde para agregar más información sobre el "por qué"! Gracias por toda esta información
Labynocle
En algún momento debe ser coherente en la estructura de su base de datos. Incluso si 2 columnas no tienen una relación, pueden tener una relación desde el punto de vista del concepto, por ejemplo, verifique el modelo EAV.
Alexandre
36

Estaba enfrentando el mismo problema al intentar truncar un VARCHAR de 32 a 8 y obtener el ERROR: value too long for type character varying(8). Quiero mantenerme lo más cerca posible de SQL porque estoy usando una estructura similar a JPA hecha por nosotros mismos que podríamos tener que cambiar a diferentes DBMS de acuerdo con las elecciones del cliente (PostgreSQL es el predeterminado). Por lo tanto, no quiero usar el truco de alterar las tablas del sistema.

Terminé usando la USINGdeclaración en ALTER TABLE:

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

Como señaló @raylu, ALTERadquiere un bloqueo exclusivo en la mesa para que todas las demás operaciones se retrasen hasta que se complete.

Matthieu
fuente
2
el ALTERadquiere un bloqueo exclusivo en la mesa y evita todas las demás operaciones
raylu
8

Agregar nueva columna y reemplazar una nueva por la anterior funcionó para mí, en redshift postgresql, consulte este enlace para obtener más detalles 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;
peleas
fuente
7

Aquí está el caché de la página descrita por Greg Smith. En caso de que también muera, la declaración alter se ve así:

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

Cuando su tabla es TABLE1, la columna es COL1 y desea establecerla en 35 caracteres (el +4 es necesario para fines heredados de acuerdo con el enlace, posiblemente la sobrecarga a la que hace referencia AH en los comentarios).

Tom
fuente
7

Si coloca el alter en una transacción, la tabla no debe bloquearse:

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

Esto funcionó para mí increíblemente rápido, unos segundos en una mesa con más de 400k filas.

Jacktrade
fuente
55
¿Por qué esperaría que el contenedor de transacciones explícitas cambie el comportamiento de bloqueo de la ALTERinstrucción? No lo hace.
Erwin Brandstetter
pruébelo usted mismo, con y sin el contenedor de transacciones, notará una gran diferencia.
jacktrade
2
Su respuesta es incorrecta en el director. Cualquier instrucción DDL sin envoltorio de transacción explícito se ejecuta implícitamente dentro de una transacción. El único efecto posible de la transacción explícita es que los bloqueos se mantienen más tiempo , hasta el explícito COMMIT. El reiniciador solo tiene sentido si desea colocar más comandos en la misma transacción.
Erwin Brandstetter
tienes toda la razón, pero insisto: pruébalo, continúa. y luego pregunte por qué no funciona de la misma manera.
jacktrade
No ayudó en Postgres 9.3.
Noumenon
1

He encontrado una manera muy fácil de cambiar el tamaño, es decir, la anotación @Size (min = 1, max = 50) que forma parte de "import javax.validation.constraints", es decir, "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
fuente
Gracias por tu publicación! No utilices firmas / lemas en tus publicaciones. Su carpeta de usuario cuenta como su firma, y ​​puede usar su perfil para publicar cualquier información sobre usted que desee. Preguntas frecuentes sobre firmas / lemas
Andrew Barber
0

Intente ejecutar la siguiente tabla alter:

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