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

问题描述 投票:0回答:11

问题很简单。如何将列

x
添加到表
y
,但仅当
x
列不存在时?我找到了唯一的解决方案here如何检查列是否存在。

SELECT column_name 
FROM information_schema.columns 
WHERE table_name='x' and column_name='y';
postgresql postgresql-9.1
11个回答
615
投票

对于 Postgres 9.6 这可以使用选项

if not exists

来完成
ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;

157
投票

这是使用“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 注入攻击。


36
投票

Postgres 9.6添加了

IF NOT EXISTS

ALTER TABLE tbl
ADD COLUMN IF NOT EXISTS column_name;`

所以现在这基本上已经过时了。您可以在旧版本中使用它,或者使用它的变体来检查不仅仅是列名称。


CREATE OR REPLACE function f_add_col(_tbl regclass, _col  text, _type regtype)
  RETURNS bool
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF EXISTS (SELECT 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$;

致电:

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

成功则返回

true
,否则返回
false
(列已存在)。
引发无效表或类型名称的异常。

为什么要另一个版本?

  • 这可以通过

    DO
    语句来完成,但是
    DO
    语句不能返回任何内容。如果要重复使用,我会创建一个函数。

  • 我使用对象标识符类型

    regclass
    regtype
    来表示
    _tbl
    _type
    ,它a)防止SQL注入,b)立即检查两者的有效性(最便宜的方法)。列名称
    _col
    仍需使用
    EXECUTE
    进行
    quote_ident()
    清理。参见:

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

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

  • 我查询

    pg_catalog
    而不是
    information_schema
    。详细解释:

  • 包含

    EXCEPTION
    子句的块要慢得多。
    这更简单、更快。 说明书:

提示

包含

EXCEPTION
子句的块明显更多 进入和退出比没有进入和退出的街区昂贵。 因此,如无必要,请勿使用
EXCEPTION


22
投票

以下选择查询将返回

true/false
,使用
EXISTS()
函数。

EXISTS():
EXISTS 的参数是任意 SELECT 语句,或者 子查询。评估子查询以确定它是否返回 任何行。如果它至少返回一行,则 EXISTS 的结果是 “真的”;如果子查询没有返回行,则 EXISTS 的结果是 “假”

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

8
投票

对于那些使用 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;
$$

1
投票

这基本上是 sola 的解决方案,但只是做了一些清理。它足够不同,我不仅仅是想“改进”他的解决方案(另外,我认为这有点粗鲁)。

主要区别在于它使用 EXECUTE 格式。我认为这更简洁,但我相信这意味着您必须使用 PostgresSQL 9.1 或更高版本。

这已经在 9.1 上测试并且有效。注意:如果 schema/table_name/or 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 $$;

-4
投票

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

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

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


-5
投票

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

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

ALTER TABLE x ADD COLUMN y int;

在其中放置对“x”和“y”有用的内容,当然还有我使用 int 的合适数据类型。

© www.soinside.com 2019 - 2024. All rights reserved.