138 votos

Postgresql - cambiar el tamaño de una columna varchar a una longitud menor

Tengo una pregunta sobre el comando ALTER TABLE en una tabla realmente grande (casi 30 millones de filas). Una de sus columnas es un varchar(255) y me gustaría cambiar su tamaño a un 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 problema si el proceso es muy largo, pero parece que mi tabla ya no es legible durante el comando ALTER TABLE. ¿Existe una manera más inteligente? ¿Quizás agregar una nueva columna, copiar los valores de la columna antigua, eliminar la columna antigua y finalmente renombrar la nueva?

Nota: Estoy utilizando PostgreSQL 9.0.

69voto

Greg Smith Puntos 5443

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

Asegúrate de leer la sección de Tipos de caracteres en la documentación antes de hacer este cambio. Hay todo tipo de casos extraños de los que debes ser consciente aquí. La verificación de la longitud se realiza cuando se almacenan los valores en las filas. Si hackeas un límite inferior allí, eso no reducirá en absoluto el tamaño de los valores existentes. Sería prudente hacer un escaneo de toda la tabla en busca de filas donde la longitud del campo sea >40 caracteres después de hacer el cambio. Tendrás que averiguar cómo truncar esos manualmente, por lo que volverás a tener bloqueos solo en los que sean demasiado grandes, porque si alguien intenta actualizar algo en esa fila, lo rechazará por ser demasiado grande ahora, en el momento en que vaya a almacenar la nueva versión de la fila. La diversión está asegurada para el usuario.

VARCHAR es un tipo terrible que existe en PostgreSQL solo para cumplir con su asociada parte terrible del estándar SQL. Si no te importa la compatibilidad con múltiples bases de datos, considera almacenar tus datos como TEXT y agregar una restricción para limitar su longitud. Las restricciones se pueden modificar sin este problema de bloqueo/escritura de tabla, y pueden hacer más verificaciones de integridad que solo la débil verificación de longitud.

45voto

Sergey Puntos 3754

¡Ok, probablemente llegué tarde a la fiesta, PERO...

¡NO HAY NECESIDAD DE REDIMENSIONAR LA COLUMNA EN TU CASO!

Postgres, a diferencia de algunas otras bases de datos, es lo suficientemente inteligente como para usar solo el espacio necesario para ajustar la cadena (incluso utilizando compresión para cadenas más largas), por lo que incluso si tu columna está declarada como VARCHAR(255) - si almacenas cadenas de 40 caracteres en la columna, el uso del espacio será de 40 bytes + 1 byte de sobrecarga.

El requisito de almacenamiento para un string corto (hasta 126 bytes) es de 1 byte más la cadena real, que incluye el relleno de espacios en el caso de character. Cadenas más largas tienen 4 bytes de sobrecarga en lugar de 1. Las cadenas largas son comprimidas automáticamente por el sistema, por lo que el requerimiento físico en disco podría ser menor. Valores muy largos también se almacenan en tablas secundarias 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 utiliza para verificar el tamaño de los valores que se insertan, no afecta el diseño en disco. De hecho, los campos VARCHAR y TEXT se almacenan de la misma manera en Postgres.

34voto

Matthieu Puntos 1603

Me encontré con 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 cercano posible a SQL porque estoy usando una estructura similar a JPA hecha por mí mismo que podríamos tener que cambiar a diferentes sistemas de gestión de bases de datos según las elecciones del cliente (siendo PostgreSQL el predeterminado). Por lo tanto, no quiero usar el truco de alterar las tablas del sistema.

Terminé usando la declaración USING en el ALTER TABLE:

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

Como @raylu señaló, ALTER adquiere un bloqueo exclusivo en la tabla por lo que todas las demás operaciones se retrasarán hasta que se complete.

7voto

spats Puntos 118

Añadir una nueva columna y reemplazarla con la antigua funcionó para mí, en redshift postgresql, consulta este enlace para 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;

7voto

Tom Puntos 7393

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

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

Donde tu tabla es TABLE1, la columna es COL1 y quieres establecerla en 35 caracteres (el +4 es necesario por propósitos de legado según el enlace, posiblemente el sobrecosto mencionado por A.H. en los comentarios).

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X