如果在PostgreSQL上不存在列,如何添加列?

mar*_*osh 128 postgresql postgresql-9.1

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

SELECT column_name 
FROM information_schema.columns 
WHERE table_name='x' and column_name='y';
Run Code Online (Sandbox Code Playgroud)

a_h*_*ame 298

使用Postgres 9.6,可以使用该选项完成if not exists

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
Run Code Online (Sandbox Code Playgroud)

  • 堆栈溢出确实应该支持更改已接受的答案。 (4认同)
  • 甜。不幸的是,还没有“如果不存在则添加约束”。 (3认同)
  • 为什么这个答案在页面底部,所以比其他选项好得多。 (3认同)

Mat*_*ood 124

这是一个使用"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;
$$
Run Code Online (Sandbox Code Playgroud)

您不能将这些作为参数传递,您需要在客户端的字符串中进行变量替换,但这是一个自包含的查询,只有在列已经存在时才会发出消息,如果不存在则添加将继续失败的其他错误(如无效的数据类型).

如果这些是来自外部源的随机字符串,我建议不要执行任何这些方法.无论您使用何种方法(作为查询执行的cleint端或服务器端动态字符串),它都会成为灾难的一种方法,因为它会打开您的SQL注入攻击.

  • 用END结束; $$`是语法错误(Postgres 9.3),我不得不使用`END $$;` (8认同)
  • 好的方法,但为什么嵌套的BEGIN/END块?对我来说,单层工作正常.另外在末尾添加分号($$;)使得该语句对于psql是明确的. (5认同)
  • `DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree(type); duplicate_table那么异常提升通知'指数存在.'; 结束; END; $$;``CREATE INDEX`中的相同方法;)感谢您的回答, (4认同)

Erw*_*ter 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;
Run Code Online (Sandbox Code Playgroud)

呼叫:

SELECT f_add_col('public.kat', 'pfad1', 'int');
Run Code Online (Sandbox Code Playgroud)

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

为何要另一个版本

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

  • 我使用对象标识符类型 regclassregtypefor _tbl,_type其中a)防止SQL注入和b)立即检查两者的有效性(最便宜的方式).列名_col仍然被用于消毒EXECUTEquote_ident().在这个相关答案中有更多解释:

  • format()需要Postgres 9.1+.对于旧版本手动连接:

    EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以对表名进行架构限定,但不必如此.
    您可以在函数调用中双引号标识符以保留驼峰大小写和保留字(但您不应该使用任何此类标记).

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

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

提示:包含EXCEPTION子句的块进入和退出的成本明显高于没有子句的块.因此,请勿在不需要的情况下使用EXCEPTION.


Viv*_* S. 17

以下select查询将返回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')
Run Code Online (Sandbox Code Playgroud)

并使用以下动态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
$$
Run Code Online (Sandbox Code Playgroud)

  • 多个模式中可以存在重复的表名和列名。 (2认同)

小智 8

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

ALTER TABLE if exists <tablename> add if not exists <columnname> <columntype>
Run Code Online (Sandbox Code Playgroud)

  • 此语法在 PG 9.5.7 上无效(或任何 PG 版本,它会出现) (2认同)