Respostas:
Aqui está uma versão curta e agradável usando a instrução "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;
$$
Você não pode passar isso como parâmetros, precisará fazer a substituição de variáveis na string no lado do cliente, mas essa é uma consulta independente que somente emite uma mensagem se a coluna já existir, adiciona se não existir e continuará a falhar em outros erros (como um tipo de dados inválido).
Eu não recomendo executar QUALQUER desses métodos se forem seqüências aleatórias provenientes de fontes externas. Não importa qual método você use (cadeias dinâmicas cleint ou do lado do servidor executadas como consultas), seria uma receita para o desastre, pois abre para ataques de injeção de SQL.
DO $$
falha. Eu tentei o DO $$;
que também falhou, até começar o bloco com o DO $$DECLARE r record;
qual é dado um exemplo nos documentos do dev postgres .
END; $$
é um erro de sintaxe (Postgres 9.3), eu tive que usar em END $$;
vez disso
EXCEPTION
) é um pouco mais geral e pode ser empregada para tarefas que não possuem IF NOT EXISTS
sintaxe - por exemplo ALTER TABLE ... ADD CONSTRAINT
.
Com o Postgres 9.6, isso pode ser feito usando a opçãoif not exists
ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
ADD CONSTRAINT IF NOT EXISTS
ainda não há .
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;
Ligar:
SELECT f_add_col('public.kat', 'pfad1', 'int');
Retorna TRUE
com sucesso, caso contrário FALSE
(a coluna já existe).
Gera uma exceção para nome de tabela ou tipo inválido.
Isso pode ser feito com uma DO
instrução, mas as DO
instruções não podem retornar nada. E se for para uso repetido, eu criaria uma função.
Eu uso os tipos de identificador de objeto regclass
e regtype
para _tbl
e _type
que a) impede a injeção de SQL eb) verifica a validade de ambos imediatamente (da maneira mais barata possível). O nome da coluna _col
ainda precisa ser limpo EXECUTE
com quote_ident()
. Mais explicações nesta resposta relacionada:
format()
requer o Postgres 9.1+. Para versões mais antigas concatenar manualmente:
EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
Você pode qualificar o nome da tabela com esquema, mas não precisa.
Você pode citar duas vezes os identificadores na chamada de função para preservar palavras reservadas e em maiúsculas e minúsculas (mas não deve usar nada disso).
Eu pergunto em pg_catalog
vez do information_schema
. Explicação detalhada:
Blocos contendo uma EXCEPTION
cláusula como a resposta atualmente aceita são substancialmente mais lentos. Isso geralmente é mais simples e rápido. A documentação:
Dica: Um bloco contendo uma
EXCEPTION
cláusula é significativamente mais caro para entrar e sair do que um bloco sem uma. Portanto, não useEXCEPTION
sem necessidade.
DO
declaração, uma pequena modificação para aceitar DEFAULT
e isso funcionou perfeitamente!
A consulta de seleção a seguir retornará true/false
, usando a EXISTS()
função
EXISTS () :
O argumento EXISTS é uma instrução SELECT arbitrária ou subconsulta. A subconsulta é avaliada para determinar se retorna alguma linha. Se retornar pelo menos uma linha, o resultado de EXISTS será "verdadeiro"; se a subconsulta não retornar linhas, o resultado de EXISTS será "false"
SELECT EXISTS(SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'x'
AND column_name = 'y');
e use a seguinte instrução SQL dinâmica para alterar sua tabela
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
$$
a função abaixo verificará a coluna, se existir, retorne a mensagem apropriada; caso contrário, a coluna será adicionada à tabela.
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;
$$
Esta é basicamente a solução da sola, mas apenas limpou um pouco. É diferente o suficiente para eu não apenas querer "melhorar" sua solução (mais, acho que isso é rude).
A principal diferença é que ele usa o formato EXECUTE. O que eu acho que é um pouco mais limpo, mas acredito que significa que você deve estar no PostgresSQL 9.1 ou mais recente.
Isso foi testado na 9.1 e funciona. Nota: Irá gerar um erro se o esquema / nome_tabela / ou tipo_de_dados for inválido. Isso pode "consertar", mas pode ser o comportamento correto em muitos casos.
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';
uso:
select add_column('public', 'foo', 'bar', 'varchar(30)');
Podem ser adicionados aos scripts de migração que invocam a função e descartam quando concluídos.
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();
No meu caso, pela forma como foi criado, é um pouco difícil para nossos scripts de migração atravessar esquemas diferentes.
Para contornar isso, usamos uma exceção que capturou e ignorou o erro. Isso também teve o bom efeito colateral de ser muito mais fácil de olhar.
No entanto, tenha cuidado para que as outras soluções tenham suas próprias vantagens que provavelmente superam essa solução:
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 $$;
Você pode fazer isso da seguinte maneira.
ALTER TABLE tableName drop column if exists columnName;
ALTER TABLE tableName ADD COLUMN columnName character varying(8);
Portanto, ele eliminará a coluna se ela já existir. E, em seguida, adicione a coluna à tabela específica.
Basta verificar se a consulta retornou um nome da coluna.
Caso contrário, execute algo como isto:
ALTER TABLE x ADD COLUMN y int;
Onde você coloca algo útil para 'x' e 'y' e, claro, um tipo de dados adequado onde eu usei int.
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;
a mesma abordagem emCREATE INDEX
;) Obrigado pela sua resposta,