Comment puis-je ajouter une colonne à une base de données Postgresql qui n'autorise pas les valeurs NULL?

243

J'ajoute une nouvelle colonne "NOT NULL" à ma base de données Postgresql en utilisant la requête suivante (filtrée pour Internet):

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL;

Chaque fois que j'exécute cette requête, je reçois le message d'erreur suivant:

ERROR:  column "mycolumn" contains null values

Je suis perplexe. Où est-ce que je vais mal?

REMARQUE: j'utilise principalement pgAdmin III (1.8.4), mais j'ai reçu la même erreur lorsque j'ai exécuté le SQL à partir de Terminal.

Huuuze
la source

Réponses:

400

Vous devez définir une valeur par défaut.

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL DEFAULT 'foo';

... some work (set real values as you want)...

ALTER TABLE mytable ALTER COLUMN mycolumn DROP DEFAULT;
Luc M
la source
1
Belle solution. Je n'ai pas pu accéder aux documents postgres en ligne pour une raison quelconque pour voir quelle serait la syntaxe pour cela.
Sean Bright
4
@SeanBright, vous pouvez accéder au doc ​​postgres hors ligne en faisant man ALTER_TABLE :)
allan.simon
@ allan.simon Je n'ai jamais utilisé PostgreSQL auparavant et je ne l'ai installé nulle part.
Sean Bright du
2
Pour clarifier: la valeur par défaut n'est nécessaire que pour mettre à jour les lignes existantes, c'est pourquoi elle peut être supprimée immédiatement après. Toutes les lignes existantes ont été mises à jour lorsque la table a été modifiée (ce qui peut prendre un certain temps, évidemment)
MSalters
2
Cela ne fonctionnera pas si vous souhaitez utiliser une autre colonne pour calculer la valeur initiale des lignes existantes. La réponse de j_random_hacker le permet, le rendant plus robuste.
jpmc26
82

Comme d'autres l'ont observé, vous devez soit créer une colonne nullable, soit fournir une valeur DEFAULT. Si cela n'est pas assez flexible (par exemple, si vous avez besoin que la nouvelle valeur soit calculée individuellement pour chaque ligne), vous pouvez utiliser le fait que dans PostgreSQL, toutes les commandes DDL peuvent être exécutées dans une transaction:

BEGIN;
ALTER TABLE mytable ADD COLUMN mycolumn character varying(50);
UPDATE mytable SET mycolumn = timeofday();    -- Just a silly example
ALTER TABLE mytable ALTER COLUMN mycolumn SET NOT NULL;
COMMIT;
j_random_hacker
la source
7
même dans une transaction, NOT NULL est appliqué immédiatement, il faut donc d'abord ajouter une colonne, remplir des valeurs, puis ajouter NOT NULL - comme le fait cette réponse. (testé sur postgres 9.6)
Beni Cherniavsky-Paskin
48

Étant donné que des lignes existent déjà dans la table, l' ALTERinstruction tente d'insérer NULLdans la colonne nouvellement créée pour toutes les lignes existantes. Vous devez ajouter la colonne comme autorisé NULL, puis remplir la colonne avec les valeurs souhaitées, puis la définir NOT NULLensuite.

Sean Bright
la source
7
Un exemple de comment le faire aurait été vraiment sympa. Sinon, la solution de Luc semble être plus complète et prête à l'emploi.
Victor Farazdagi
5

Vous devez soit définir une valeur par défaut, soit faire ce que Sean dit et l'ajouter sans la contrainte null jusqu'à ce que vous l'ayez remplie sur les lignes existantes.

Paul Tomblin
la source
2

La spécification d'une valeur par défaut fonctionnerait également, en supposant qu'une valeur par défaut soit appropriée.

Ryan Graham
la source
2
Cela améliorerait la réponse pour donner la syntaxe modifiée pour créer la colonne avec une valeur par défaut (pour illustration).
hardmath
1

Ou, créez une nouvelle table en tant que temp avec la colonne supplémentaire, copiez les données dans cette nouvelle table tout en la manipulant si nécessaire pour remplir la nouvelle colonne non nullable, puis échangez la table via un changement de nom en deux étapes.

Oui, c'est plus compliqué, mais vous devrez peut-être le faire de cette façon si vous ne voulez pas une grosse MISE À JOUR sur une table en direct.

alphadogg
la source
3
Je ne vous ai pas -1, mais je pense qu'il peut y avoir de subtiles difficultés avec cela - par exemple, je parie que les indices, déclencheurs et vues existants continueront de se référer au tableau d'origine même après le renommage car je pense qu'ils stockent le relid de la table (qui ne change pas) plutôt que son nom.
j_random_hacker
1
Oui, j'aurais dû dire que le nouveau tableau devrait être une copie exacte de l'original, y compris l'ajout d'indices et autres. Mon mal d'être trop bref. La raison en est qu'il existe également de subtiles difficultés à effectuer un ALTER sur une table qui peut être en direct, et parfois vous devez le mettre en scène.
alphadogg
Par exemple, en utilisant l'approche DEFAULT, vous ajouterez cette valeur par défaut à chaque ligne. Vous ne savez pas comment Postgres verrouille une table lors de cette opération. Ou, si l'ordre des colonnes est important, vous ne pouvez pas simplement ajouter une colonne avec la commande ALTER.
alphadogg
Assez bien, ALTER TABLE verrouille la table selon les documents PostgreSQL, mais votre approche non transactionnelle risque de perdre des modifications si la table est en effet en direct. Je suggérerais également que tout code qui repose sur l'ordre des colonnes est rompu (cela peut bien sûr être hors de votre contrôle).
j_random_hacker
2
Cette approche est également particulièrement problématique si la table est référencée par des index de clé étrangère car ceux-ci devraient tous être recréés également.
Aryeh Leib Taurog
0

cette requête mettra automatiquement à jour les valeurs nulles

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) DEFAULT 'whatever' NOT NULL;
jacktrade
la source
-6

Cela a fonctionné pour moi: :)

ALTER TABLE your_table_name ADD COLUMN new_column_name int;
timxor
la source
2
Il n'y a aucune NOT NULLcontrainte sur votre requête. Bien sûr, cela fonctionne.
Sylvain