【问题标题】:How to add column if not exists on PostgreSQL?如果 PostgreSQL 上不存在,如何添加列?
【发布时间】:2012-09-17 19:51:19
【问题描述】:

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

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

【问题讨论】:

    标签: postgresql postgresql-9.1


    【解决方案1】:

    Postgres 9.6 addedALTER 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 语句不能返回任何内容。如果它是为了重复使用,我会创建一个函数。

    • 我使用object identifier types regclassregtype 来表示_tbl_type,它们a) 防止SQL 注入,b) 立即检查两者的有效性(最便宜的方法)。列名_col 仍需使用quote_ident()EXECUTE 进行清理。见:

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

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

    • 我查询 pg_catalog 而不是 information_schema。详细解释:

    • 包含EXCEPTION 子句的块要慢得多。
      这更简单,更快捷。 The manual:

    提示

    包含EXCEPTION 子句的块明显更多 进出比没有的街区贵。 因此,不要在没有必要的情况下使用EXCEPTION

    【讨论】:

    • 我比我更喜欢你的解决方案!它更好、更安全、更快捷。
    • 我必须使用的 Postgres 版本没有 DO 声明,稍作修改以接受 DEFAULT 并且效果很好!
    【解决方案2】:

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

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

    如果这些方法是来自外部来源的随机字符串,我不建议使用任何这些方法。无论您使用哪种方法(作为查询执行的客户端或服务器端动态字符串),这都会导致灾难,因为它会使您面临 SQL 注入攻击。

    【讨论】:

    • 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 中的相同方法 ;) 感谢您的回答,
    • 我不确定为什么用DO $$ 启动匿名代码块会失败。我确实尝试过DO $$;,但它也失败了,直到我刚刚使用DO $$DECLARE r record; 开始块,这在dev postgres docs 的示例中给出。
    • END; $$ 结束是语法错误(Postgres 9.3),我不得不改用END $$;
    • 不错的方法,但为什么嵌套的 BEGIN/END 块?它适用于我的单层。在末尾添加分号 ($$;) 使语句对 psql 明确。
    • 这种方法 (EXCEPTION) 更通用一些,可以用于没有IF NOT EXISTS 语法的任务——例如ALTER TABLE ... ADD CONSTRAINT
    【解决方案3】:

    以下选择查询将返回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
    $$
    

    【讨论】:

    • 重复的表名和列名可以存在于多个模式中。
    • 好吧,您可能需要重写代码以考虑架构。
    【解决方案4】:

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

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

    【讨论】:

    • 此语法在 PG 9.5.7(或任何 PG 版本,它会出现)上无效
    • 对不起,我应该提到我在 x86_64-pc-linux-gnu 上使用 PostgreSQL 11.10,由 gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9) 编译,64 位。它在我的数据库实例上进行了测试。 @BrDaHa
    【解决方案5】:

    就我而言,由于它是如何创建的,我们的迁移脚本很难跨越不同的模式。

    为了解决这个问题,我们使用了一个刚刚捕获并忽略错误的异常。这还有一个很好的副作用,就是更容易查看。

    但是,请注意其他解决方案有自己的优势,可能超过此解决方案:

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

    【讨论】:

      【解决方案6】:

      对于Postgres 9.6,这可以使用选项if not exists 来完成

      ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
      

      【讨论】:

      • 甜蜜。不幸的是,还没有ADD CONSTRAINT IF NOT EXISTS
      • 为什么这个答案在页面底部,比其他选项好多了。
      • Stack-overflow 确实应该支持更改接受的答案。
      • @HenrikSommerland:允许的 - 但仅限提出问题的人。
      • @Ecksters 因为这个问题是在 2012 年提出的,比这个答案早了 4 年。除非 OP 更改它,否则这永远不会是“最佳”答案。
      【解决方案7】:

      您可以通过以下方式进行。

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

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

      【讨论】:

      • 丢失数据怎么办?
      • 您可以随时向您的客户道歉
      • 我刚刚添加了列,所以这对我来说非常方便。
      【解决方案8】:

      可以添加到迁移脚本调用函数并在完成时删除。

      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();
      

      【讨论】:

        【解决方案9】:

        这基本上是来自 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)');
        

        【讨论】:

          【解决方案10】:

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

          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 的最新成员
          【解决方案11】:

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

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

          ALTER TABLE x ADD COLUMN y int;
          

          你在哪里放了一些对“x”和“y”有用的东西,当然还有我使用 int 的合适的数据类型。

          【讨论】:

          • 你在什么环境?您的提案中是否有脚本语言?或者你在使用 PL/pgSQL?你是从 PHP/Java/etc 之类的语言执行的吗?
          • 没有脚本语言。我需要仅在 SQL 中执行此操作。我有 Java 应用程序,它在输入时获取 SQL 脚本并在选定的数据库上运行该脚本。
          • 那我建议你看看 pl/pgsql:postgresql.org/docs/9.1/static/plpgsql.html 创建一个以 column_name 和 table_name 作为参数的函数。
          猜你喜欢
          • 2014-03-17
          • 2020-11-18
          • 2011-03-15
          • 2019-04-22
          • 2018-02-02
          • 2019-03-12
          • 2013-01-01
          • 2014-11-01
          相关资源
          最近更新 更多