Postgresql - change la taille d'une colonne varchar en une longueur inférieure


154

J'ai une question sur la ALTER TABLEcommande sur une très grande table (près de 30 millions de lignes). L'une de ses colonnes est a varchar(255)et je voudrais la redimensionner en un varchar(40). En gros, je voudrais changer ma colonne en exécutant la commande suivante:

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

Je n'ai aucun problème si le processus est très long mais il semble que ma table ne soit plus lisible pendant la commande ALTER TABLE. Y a-t-il un moyen plus intelligent? Peut-être ajouter une nouvelle colonne, copier les valeurs de l'ancienne colonne, supprimer l'ancienne colonne et enfin renommer la nouvelle?

Tout indice sera grandement apprécié! Merci d'avance,

Remarque: j'utilise PostgreSQL 9.0.


11
Juste pour être clair: vous savez, cela resizingne rendra pas la table moins encombrante?
AH

même dans mon cas? Je veux dire que la colonne aura une taille maximale de 40 caractères (donc octets) au lieu de 255?
Labynocle

16
Si vous dites varchar(255)à PostgreSQL, il n'allouera pas 255 octets pour une valeur dont la longueur réelle est de 40 octets. Il allouera 40 octets (plus une surcharge interne). La seule chose qui be changed by the ALTER TABLE` est le nombre maximum d'octets que vous pouvez stocker dans cette colonne sans obtenir une erreur de PG.
AH

À propos de la surcharge AH ​​mentionnée: Quelle est la surcharge de varchar (n)?
Erwin Brandstetter

Découvrez la réponse ici pour une mise à jour dba.stackexchange.com/questions/189890/…
Evan Carroll

Réponses:


73

Il y a une description de la façon de faire cela à Redimensionner une colonne dans une table PostgreSQL sans changer de données . Vous devez pirater les données du catalogue de la base de données. La seule façon de le faire officiellement est avec ALTER TABLE, et comme vous l'avez noté, ce changement verrouille et réécrit la table entière pendant son exécution.

Assurez-vous de lire la section Types de caractères de la documentation avant de modifier cela. Toutes sortes de cas étranges à connaître ici. Le contrôle de longueur est effectué lorsque les valeurs sont stockées dans les lignes. Si vous piratez une limite inférieure, cela ne réduira pas du tout la taille des valeurs existantes. Vous seriez sage de faire un scan sur toute la table à la recherche de lignes dont la longueur du champ est> 40 caractères après avoir effectué la modification. Vous devrez trouver comment tronquer ceux-ci manuellement - vous êtes donc revenu sur certains verrous uniquement sur des verrous surdimensionnés - parce que si quelqu'un essaie de mettre à jour quoi que ce soit sur cette ligne, il le rejettera comme trop gros maintenant, au point il va stocker la nouvelle version de la ligne. La hilarité s'ensuit pour l'utilisateur.

VARCHAR est un type terrible qui n'existe dans PostgreSQL que pour se conformer à sa terrible partie associée du standard SQL. Si vous ne vous souciez pas de la compatibilité multi-bases de données, envisagez de stocker vos données sous forme de TEXTE et ajoutez une contrainte pour limiter sa longueur. Les contraintes que vous pouvez modifier sans ce problème de verrouillage / réécriture de table, et elles peuvent faire plus de vérification d'intégrité que simplement la vérification de longueur faible.


Merci pour la réponse. Je vais vérifier votre lien. Je ne m'inquiète pas de la vérification manuelle de la taille car tout mon contenu a une taille maximale de 40 caractères. J'ai besoin d'en savoir plus sur la contrainte sur TEXT parce que je pensais que VARCHAR était préférable de vérifier lentgh :)
Labynocle

6
Modifier la longueur de varchar ne réécrit pas la table. Il vérifie simplement la longueur de la contrainte par rapport à la table entière exactement comme CHECK CONSTRAINT. Si vous augmentez la longueur, il n'y a rien à faire, la prochaine insertion ou les mises à jour accepteront une plus grande longueur. Si vous diminuez la longueur et que toutes les lignes passent la nouvelle contrainte plus petite, Pg ne prend aucune autre action en plus de permettre aux insertions ou mises à jour suivantes d'écrire uniquement la nouvelle longueur.
Maniero

3
@bigown, juste pour clarifier, votre déclaration n'est vraie que pour PostgreSQL 9.2+ , pas les anciennes.
MatheusOl

12
Le lien est maintenant mort.
raarts

Pour plus d'informations sur la façon dont cela fonctionne, consultez dba.stackexchange.com/questions/189890/…
Evan Carroll

100

Dans PostgreSQL 9.1, il existe un moyen plus simple

http://www.postgresql.org/message-id/162867790801110710g3c686010qcdd852e721e7a559@mail.gmail.com

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) |

6
Notez que cela ne fonctionne que parce que vous spécifiez une taille plus grande (30> 10). Si la taille est plus petite, vous obtiendrez la même erreur que moi .
Matthieu

2
Postgres ne doit pas générer d'erreur si vous réduisez la taille de varchar via une requête ALTER TABLE à moins qu'une ou plusieurs lignes ne contiennent une valeur qui dépasse la nouvelle taille.
Dites le

@Tell, intéressant. Cela signifie-t-il que Postgres effectue une analyse complète de la table ou conserve d'une manière ou d'une autre la taille maximale dans ses statistiques?
Matthieu le

47

Ok, je suis probablement en retard à la fête, MAIS ...

IL N'Y A PAS BESOIN DE REDIMENSIONNER LA COLONNE DANS VOTRE CAS!

Postgres, contrairement à certaines autres bases de données, est assez intelligent pour n'utiliser que juste assez d'espace pour tenir la chaîne (même en utilisant la compression pour les chaînes plus longues), donc même si votre colonne est déclarée comme VARCHAR (255) - si vous stockez des chaînes de 40 caractères dans la colonne, l'utilisation de l'espace sera de 40 octets + 1 octet de surcharge.

Le stockage requis pour une chaîne courte (jusqu'à 126 octets) est de 1 octet plus la chaîne réelle, qui inclut le remplissage d'espace dans le cas d'un caractère. Les chaînes plus longues ont une surcharge de 4 octets au lieu de 1. Les chaînes longues sont automatiquement compressées par le système, de sorte que l'exigence physique sur le disque peut être moindre. Les valeurs très longues sont également stockées dans les tables d'arrière-plan afin qu'elles n'interfèrent pas avec l'accès rapide aux valeurs de colonne plus courtes.

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

La spécification de taille dans VARCHAR est uniquement utilisée pour vérifier la taille des valeurs insérées, elle n'affecte pas la disposition du disque. En fait, les champs VARCHAR et TEXT sont stockés de la même manière dans Postgres .


8
Jamais trop tard pour ajouter plus d'informations sur le "pourquoi"! Merci pour toutes ces informations
Labynocle

Parfois, vous devez être cohérent dans la structure de votre base de données. Même si 2 colonnes n'ont pas de relation, elles peuvent avoir une relation du point de vue du concept, par exemple récupérer le modèle EAV.
Alexandre

37

J'étais confronté au même problème en essayant de tronquer un VARCHAR de 32 à 8 et d'obtenir le fichier ERROR: value too long for type character varying(8). Je veux rester aussi proche de SQL que possible car j'utilise une structure de type JPA que nous pourrions avoir à passer à différents SGBD en fonction des choix du client (PostgreSQL étant celui par défaut). Par conséquent, je ne veux pas utiliser l'astuce de modifier les tables système.

J'ai fini d'utiliser la USINGdéclaration dans le ALTER TABLE:

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

Comme @raylu l'a noté, ALTERacquiert un verrou exclusif sur la table afin que toutes les autres opérations soient retardées jusqu'à ce qu'elle se termine.


2
le ALTERacquiert un verrou exclusif sur la table et empêche toutes les autres opérations
raylu

8

L'ajout d'une nouvelle colonne et le remplacement de la nouvelle par l'ancienne a fonctionné pour moi, sur redshift postgresql, reportez-vous à ce lien pour plus de détails 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;

7

Voici le cache de la page décrite par Greg Smith. Dans le cas où cela meurt également, l'instruction alter ressemble à ceci:

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

Lorsque votre table est TABLE1, la colonne est COL1 et vous voulez la définir sur 35 caractères (le +4 est nécessaire à des fins d'héritage selon le lien, éventuellement la surcharge mentionnée par AH dans les commentaires).


7

si vous mettez l'altérer dans une transaction, la table ne doit pas être verrouillée:

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

cela a fonctionné pour moi très rapidement, quelques secondes sur une table avec plus de 400 000 lignes.


5
Pourquoi vous attendez-vous à ce que le wrapper de transaction explicite modifie le comportement de verrouillage de l' ALTERinstruction? Ce n'est pas le cas.
Erwin Brandstetter

essayez vous-même, avec et sans le wrapper de transaction, vous remarquerez une énorme différence.
jacktrade

2
Votre réponse est incorrecte sur le principe. Toute instruction DDL sans wrapper de transaction explicite s'exécute implicitement dans une transaction. Le seul effet possible de la transaction explicite est que les verrous sont conservés plus longtemps - jusqu'à l'explicite COMMIT. Le wrapper n'a de sens que si vous souhaitez placer plus de commandes dans la même transaction.
Erwin Brandstetter

vous avez tout à fait raison, mais j'insiste: essayez-vous, continuez. puis demandez pourquoi ne fonctionne pas de la même manière.
jacktrade

N'a pas aidé sur Postgres 9.3.
Noumenon le

1

J'ai trouvé un moyen très simple de changer la taille à savoir l'annotation @Size (min = 1, max = 50) qui fait partie de "import javax.validation.constraints" ie "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),

.....

)

Merci pour votre message! Veuillez ne pas utiliser de signatures / slogans dans vos messages. Votre boîte utilisateur compte comme votre signature et vous pouvez utiliser votre profil pour publier toute information vous concernant que vous aimez. FAQ sur les signatures / slogans
Andrew Barber

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.