web-dev-qa-db-fra.com

Comment ajouter une colonne s'il n'en existe pas sur PostgreSQL?

La question est simple. Comment ajouter la colonne x à la table y, mais uniquement lorsque la colonne x n'existe pas? J'ai trouvé que la solution ici comment vérifier si la colonne existe.

SELECT column_name 
FROM information_schema.columns 
WHERE table_name='x' and column_name='y';
118
marioosh

Voici une version courte en utilisant l'instruction "DO":

DO $$ 
    BEGIN
        BEGIN
            ALTER TABLE <table_name> ADD COLUMN <column_name> <column_type>;
        EXCEPTION
            WHEN duplicate_column THEN RAISE NOTICE 'column <column_name> already exists in <table_name>.';
        END;
    END;
$$

Vous ne pouvez pas les transmettre en tant que paramètres, vous devrez effectuer une substitution de variable dans la chaîne côté client, mais il s'agit d'une requête autonome qui n'émet un message que si la colonne existe déjà, ajoute si ce n'est pas le cas et continuera à échouer sur d'autres erreurs (comme un type de données non valide).

Je ne recommande pas de faire AUCUNE de ces méthodes s'il s'agit de chaînes aléatoires provenant de sources externes. Quelle que soit la méthode utilisée (chaînes dynamiques côté serveur ou côté serveur exécutées sous forme de requêtes), ce serait une recette pour un sinistre, car cela vous ouvrirait la voie aux attaques par injection SQL.

118
Matthew Wood

Avec Postgres 9.6 , vous pouvez utiliser l’option if not exists

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
262
CREATE OR REPLACE function f_add_col(_tbl regclass, _col  text, _type regtype)
  RETURNS bool AS
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM pg_attribute
              WHERE  attrelid = _tbl
              AND    attname = _col
              AND    NOT attisdropped) THEN
      RETURN FALSE;
   ELSE
      EXECUTE format('ALTER TABLE %s ADD COLUMN %I %s', _tbl, _col, _type);
      RETURN TRUE;
   END IF;
END
$func$  LANGUAGE plpgsql;

Appel:

SELECT f_add_col('public.kat', 'pfad1', 'int');

Retourne TRUE en cas de succès, sinon FALSE (la colonne existe déjà).
Déclenche une exception pour un nom de table ou de type non valide.

Pourquoi une autre version?

  • Cela pourrait être fait avec une instruction DO, mais les instructions DO ne peuvent rien retourner. Et si c'est pour un usage répété, je créerais une fonction.

  • J'utilise les types d'identifiant d'objetregclass et regtype pour _tbl Et _type, Ce qui a) empêche l'injection SQL et b) vérifie validité des deux immédiatement (moyen le moins cher possible). Le nom de colonne _col Doit encore être nettoyé pour EXECUTE avec quote_ident(). Plus d'explications dans cette réponse:

  • format() requiert Postgres 9.1+. Pour les versions antérieures concaténer manuellement:

    EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
    
  • Vous pouvez qualifier votre nom de table par schéma, mais ce n'est pas obligatoire.
    Vous pouvez double-citer les identifiants dans l'appel de fonction pour préserver la casse du chameau et les mots réservés (mais vous ne devriez en aucun cas l'utiliser).

  • J'interroge pg_catalog Au lieu de information_schema. Explication détaillée:

  • Les blocs contenant une clause EXCEPTION comme la réponse actuellement acceptée sont considérablement plus lents. Ceci est généralement plus simple et plus rapide. La documentation:

Astuce: un bloc contenant une clause EXCEPTION coûte nettement plus cher à entrer et à sortir qu'un bloc sans un. Par conséquent, n'utilisez pas EXCEPTION sans nécessité.

21
Erwin Brandstetter

La requête sélectionnée suivante renverra true/false, En utilisant EXISTS() fonction.

EXISTS () :
L'argument de EXISTS est une instruction ou une sous-requête SELECT arbitraire. La sous-requête est évaluée pour déterminer si elle renvoie des lignes. S'il renvoie au moins une ligne, le résultat de EXISTS est "true"; si la sous-requête ne renvoie aucune ligne, le résultat de EXISTS est "false"

SELECT EXISTS(
SELECT column_name 
FROM information_schema.columns 
WHERE table_schema='public' 
  and table_name='x' 
  and column_name='y')

et utilisez l'instruction SQL dynamique suivante pour modifier votre table

DO
$$
BEGIN
IF not EXISTS (SELECT column_name 
               FROM information_schema.columns 
               WHERE table_schema='public' and table_name='x' and column_name='y') THEN
alter table x add column y int default null ;
else
raise NOTICE 'Already exists';
END IF;
END
$$
17
Vivek S.

la fonction ci-dessous vérifiera la colonne si elle existe renvoie le message approprié, sinon elle ajoutera la colonne à la table.

create or replace function addcol(schemaname varchar, tablename varchar, colname varchar, coltype varchar)
returns varchar 
language 'plpgsql'
as 
$$
declare 
    col_name varchar ;
begin 
      execute 'select column_name from information_schema.columns  where  table_schema = ' ||
      quote_literal(schemaname)||' and table_name='|| quote_literal(tablename) || '   and    column_name= '|| quote_literal(colname)    
      into   col_name ;   

      raise info  ' the val : % ', col_name;
      if(col_name is null ) then 
          col_name := colname;
          execute 'alter table ' ||schemaname|| '.'|| tablename || ' add column '|| colname || '  ' || coltype; 
      else
           col_name := colname ||' Already exist';
      end if;
return col_name;
end;
$$
1
solaimuruganv

Ceci est fondamentalement la solution de sola, mais vient de nettoyer un peu. C'est suffisamment différent pour que je ne veuille pas simplement "améliorer" sa solution (en plus, je pense que c'est impoli).

La principale différence est qu’il utilise le format EXECUTE. Ce qui, à mon avis, est un peu plus propre, mais je pense que vous devez être sur PostgresSQL 9.1 ou plus récent.

Cela a été testé sur 9.1 et fonctionne. Remarque: Une erreur est générée si le schéma/nom_table/ou le type de données n'est pas valide. Cela pourrait "corrigé", mais pourrait être le comportement correct dans de nombreux cas.

CREATE OR REPLACE FUNCTION add_column(schema_name TEXT, table_name TEXT, 
column_name TEXT, data_type TEXT)
RETURNS BOOLEAN
AS
$BODY$
DECLARE
  _tmp text;
BEGIN

  EXECUTE format('SELECT COLUMN_NAME FROM information_schema.columns WHERE 
    table_schema=%L
    AND table_name=%L
    AND column_name=%L', schema_name, table_name, column_name)
  INTO _tmp;

  IF _tmp IS NOT NULL THEN
    RAISE NOTICE 'Column % already exists in %.%', column_name, schema_name, table_name;
    RETURN FALSE;
  END IF;

  EXECUTE format('ALTER TABLE %I.%I ADD COLUMN %I %s;', schema_name, table_name, column_name, data_type);

  RAISE NOTICE 'Column % added to %.%', column_name, schema_name, table_name;

  RETURN TRUE;
END;
$BODY$
LANGUAGE 'plpgsql';

usage:

select add_column('public', 'foo', 'bar', 'varchar(30)');
1
David S

Vous pouvez le faire de la manière suivante.

ALTER TABLE tableName drop column if exists columnName; 
ALTER TABLE tableName ADD COLUMN columnName character varying(8);

Donc, il lâchera la colonne si elle existe déjà. Et puis ajoutez la colonne à la table particulière.

0
parthivrshah

Peut être ajouté aux scripts de migration invoquer la fonction et déposer lorsque cela est fait.

create or replace function patch_column() returns void as
$$
begin
    if exists (
        select * from information_schema.columns
            where table_name='my_table'
            and column_name='missing_col'
     )
    then
        raise notice 'missing_col already exists';
    else
        alter table my_table
            add column missing_col varchar;
    end if;
end;
$$ language plpgsql;

select patch_column();

drop function if exists patch_column();
0
user645527

Dans mon cas, pour la raison pour laquelle il a été créé, il est un peu difficile pour nos scripts de migration de traiter différents schémas.

Pour contourner ce problème, nous avons utilisé une exception qui a juste attrapé et ignoré l'erreur. Cela a également eu pour effet secondaire d'être beaucoup plus facile à regarder.

Cependant, soyez prudent car les autres solutions ont leurs propres avantages qui surpassent probablement cette solution:

DO $$
BEGIN
  BEGIN
    ALTER TABLE IF EXISTS bobby_tables RENAME COLUMN "dckx" TO "xkcd";
  EXCEPTION
    WHEN undefined_column THEN RAISE NOTICE 'Column was already renamed';
  END;
END $$;
0
ThinkBonobo