Réponses:
Voici une version courte et douce 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 passer 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 d'échouer sur d'autres erreurs (comme un type de données invalide).
Je ne recommande aucune de ces méthodes s'il s'agit de chaînes aléatoires provenant de sources externes. Quelle que soit la méthode que vous utilisez (chaînes dynamiques côté clé ou côté serveur exécutées sous forme de requêtes), ce serait une recette pour le désastre car cela vous ouvre aux attaques par injection SQL.
DO $$
échoue. J'ai essayé DO $$;
ce qui échoue aussi, jusqu'à ce que je viens de démarrer le bloc avec DO $$DECLARE r record;
lequel est donné un exemple sur les docs dev postgres .
END; $$
est une erreur de syntaxe (Postgres 9.3), j'ai dû utiliser à la END $$;
place
EXCEPTION
) est un peu plus générale et peut être utilisée pour des tâches sans IF NOT EXISTS
syntaxe, par exemple ALTER TABLE ... ADD CONSTRAINT
.
Avec Postgres 9.6, cela peut être fait en utilisant l'optionif not exists
ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
ADD CONSTRAINT IF NOT EXISTS
encore.
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.
Cela peut être fait avec une DO
instruction, mais les DO
instructions ne peuvent rien renvoyer. Et si c'est pour une utilisation répétée, je créerais une fonction.
On utilise les types d'identificateur d'objet regclass
et regtype
pour , _tbl
et _type
qui a) empêche contrôles injection SQL et b) la validité de la fois immédiatement ( le moins cher de manière possible). Le nom de la colonne _col
doit encore être nettoyé pour EXECUTE
avec quote_ident()
. Plus d'explications dans cette réponse connexe:
format()
nécessite Postgres 9.1+. Pour les anciennes versions concaténer manuellement:
EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
Vous pouvez qualifier de schéma le nom de votre table, 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 pas utiliser tout cela de toute façon).
Je demande à la pg_catalog
place du fichier information_schema
. Explication détaillée:
Les blocs contenant une EXCEPTION
clause comme la réponse actuellement acceptée sont sensiblement plus lents. C'est généralement plus simple et plus rapide. La documentation:
Conseil: un bloc contenant une
EXCEPTION
clause est beaucoup plus coûteux à entrer et à sortir qu'un bloc sans clause. Par conséquent, ne l'utilisez pasEXCEPTION
sans nécessité.
DO
déclaration, une légère modification à accepter DEFAULT
et cela a parfaitement fonctionné!
La requête de sélection suivante reviendra true/false
, en utilisant la EXISTS()
fonction.
EXISTS () :
l'argument de EXISTS est une instruction SELECT arbitraire ou une sous-requête. 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
$$
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;
$$
C'est essentiellement la solution de sola, mais juste un peu nettoyée. C'est assez différent que je ne voulais pas simplement "améliorer" sa solution (en plus, je pense que c'est grossier).
La principale différence est qu'il utilise le format EXECUTE. Ce qui, je pense, est un peu plus propre, mais je crois que cela signifie que vous devez être sur PostgresSQL 9.1 ou plus récent.
Cela a été testé sur 9.1 et fonctionne. Remarque: Cela générera une erreur si le schéma / nom_table / ou le type_données ne sont pas valides. Cela pourrait "être 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)');
Peut être ajouté aux scripts de migration, invoquer la fonction et supprimer une fois terminé.
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();
Dans mon cas, pour la manière dont il a été créé, il est un peu difficile pour nos scripts de migration de traverser différents schémas.
Pour contourner ce problème, nous avons utilisé une exception qui vient de capturer et ignorer l'erreur. Cela a également eu le bel effet secondaire d'être beaucoup plus facile à regarder.
Cependant, sachez que les autres solutions ont leurs propres avantages qui l'emportent probablement sur 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 $$;
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 supprimera la colonne si elle existe déjà. Et puis ajoutez la colonne à une table particulière.
Vérifiez simplement si la requête a renvoyé un nom_colonne.
Sinon, exécutez quelque chose comme ceci:
ALTER TABLE x ADD COLUMN y int;
Où vous mettez quelque chose d'utile pour 'x' et 'y' et bien sûr un type de données approprié où j'ai utilisé int.
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;
la même approche dansCREATE INDEX
;) Merci pour votre réponse,