如果PostgreSQL中不存在该如何添加列?


145

问题很简单。如何将列添加x到表中y,但仅当x列不存在时才添加?我在这里只找到解决方案如何检查列是否存在。

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

Answers:


133

这是使用“ 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;
$$

您不能将它们作为参数传递,您需要在客户端的字符串中进行变量替换,但这是一个自包含的查询,如果该列已经存在,则仅发出一条消息;如果不存在,则添加该消息,并且会因其他错误(例如无效的数据类型)而继续失败。

如果这些方法是来自外部来源的随机字符串,则我不建议使用任何这些方法。无论使用哪种方法(作为查询执行的服务器端或服务器端动态字符串),由于它使您容易受到SQL注入攻击,因此这将是灾难的根源。


4
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;同样的方法CREATE INDEX;)谢谢您的回答
marioosh 2012年

不知道为什么仅使用来启动匿名代码块会DO $$失败。我也尝试DO $$;过失败,直到我刚刚开始DO $$DECLARE r record;dev postgres docs上的示例中给出的代码块为止。
nemesisfixx

9
用结束END; $$是语法错误(Postgres 9.3),我不得不END $$;改用
LightSystem

5
好的方法,但是为什么嵌套的BEGIN / END块呢?它对我来说只有一层就可以了。另外在末尾添加一个分号($$;)使该语句对于psql来说是明确的。
Shane 2014年

1
这种方法(EXCEPTION)较为通用,可以用于没有IF NOT EXISTS语法的任务-例如ALTER TABLE ... ADD CONSTRAINT
Tomasz Gandor

389

使用Postgres 9.6可以使用if not exists

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;

4
甜。不幸的是,还没有ADD CONSTRAINT IF NOT EXISTS
Tomasz Gandor

4
为什么此答案在页面底部,所以比其他选项好得多。
Ecksters,

只是出于好奇:这是否会导致对表的访问锁定(在生产数据库中的大型表上运行时需要维护窗口)?
哈桑·拜格,

4
堆栈溢出确实应该支持更改可接受的答案。
亨里克·索默兰

@HenrikSommerland:这允许的-但只能通过谁问这个问题的人。
a_horse_with_no_name

22
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;

呼叫:

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

返回TRUE成功,否则FALSE(列已存在)。
为无效的表或类型名称引发异常。

为什么要使用其他版本?

  • 这可以通过一条DO语句来完成,但是DO语句不能返回任何内容。如果要重复使用,我将创建一个函数。

  • 我用的对象标识符类型 regclassregtype用于_tbl_type其中a)防止SQL注入和b)立即两者的检查有效性(最便宜的可能的方式)。列名_col仍然被用于消毒EXECUTEquote_ident()。此相关答案中的更多说明:

  • format()需要Postgres 9.1+。对于较旧的版本,请手动进行串联:

    EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
  • 您可以对表名进行模式限定,但是不必这样做。
    您可以在函数调用中对标识符加双引号以保留驼峰式和保留字(但无论如何您都不应使用其中的任何一种)。

  • 我查询pg_catalog而不是information_schema。详细说明:

  • 包含EXCEPTION类似于当前接受的答案的子句的块要慢得多。这通常更简单,更快。文档:

提示:与没有EXCEPTION子句的块相比,包含子句的块的进入和退出成本明显更高。因此,不要使用EXCEPTION而无需使用。


我比我更喜欢您的解决方案!更好,更安全,更快。
David S

我要使用的Postgres版本没有DO声明,可以稍加修改即可接受,DEFAULT并且效果很好!
renab

18

选择查询之后将true/false使用EXISTS()函数返回。

EXISTS()EXISTS
的参数是任意的SELECT语句或子查询。计算子查询以确定是否返回任何行。如果返回至少一行,则EXISTS的结果为“ true”;如果子查询不返回任何行,则EXISTS的结果为“ false”

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

并使用以下动态SQL语句更改表

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

2
重复的表名和列名可以存在于多个模式中。
Mike Sherrill'Cat Recall'14

1
好吧,您可能想要重写代码以说明架构。
Mike Sherrill'Cat Recall'14

2

对于使用Postgre 9.5+的用户(我相信大多数人都这样做),有一个非常简单干净的解决方案

ALTER TABLE if exists <tablename> add if not exists <columnname> <columntype>

1

下面的函数将检查该列是否存在,并返回适当的消息,否则它将将该列添加到表中。

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;
$$

给我一个非常合理的答案,特别是因为DO是postgres的最新成员-John
Powell

1

基本上,这是sola的解决方案,但只是进行了一些整理。足够不同的是,我不只是想“改进”他的解决方案(而且,我认为这很粗鲁)。

主要区别在于它使用EXECUTE格式。我认为这更干净一些,但是我认为您必须使用PostgresSQL 9.1或更高版本。

这已经在9.1上进行了测试并且可以工作。注意:如果schema / table_name /或data_type无效,则会引发错误。那可以“解决”,但在许多情况下可能是正确的行为。

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';

用法:

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

0

可以添加到迁移脚本调用函数中,完成后删除。

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

就我而言,由于其创建原因,我们的迁移脚本很难跨越不同的架构。

要解决此问题,我们使用了一个仅捕获并忽略该错误的异常。这也带来了很好的副作用,即易于查看。

但是,请警惕其他解决方案具有其自身优势,而这些优势可能会超过该解决方案:

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 $$;

-1

您可以按照以下方式进行操作。

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

因此,如果该列已经存在,它将删除该列。然后将列添加到特定的表。


17
丢失数据呢?
Aliaksei Ramanau

48
您可能总是向您的客户道歉
konzo

我刚刚添加了该列,因此对我来说非常方便。
本体

-4

只需检查查询是否返回column_name。

如果没有,请执行以下操作:

ALTER TABLE x ADD COLUMN y int;

在哪里放置了对“ x”和“ y”有用的东西,以及在我使用int的地方当然是合适的数据类型。


你在什么环境 您的提案中是否有脚本语言?还是您正在使用PL / pgSQL?您是从PHP / Java / etc等语言执行的吗?
欧文·莫勒

没有脚本语言。我只需要在SQL中执行此操作。我有一个Java应用程序,该应用程序在输入时获得SQL脚本并在选定的数据库上运行该脚本。
marioosh 2012年

2
然后,我建议您研究pl / pgsql:postgresql.org/docs/9.1/static/plpgsql.html创建一个以column_name和table_name作为参数的函数。
Erwin Moller 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.