【问题标题】:Rename column only if exists仅在存在时重命名列
【发布时间】:2018-06-14 14:21:52
【问题描述】:

PostgreSQL 不允许

ALTER TABLE t RENAME COLUMN IF EXISTS c1 TO c2

...或类似的东西。但是,能够编写修改数据库结构的脚本非常方便,无需先检查它是否已经运行,就可以再次运行。

我如何编写一个 PostgreSQL 函数来做到这一点?

【问题讨论】:

  • plpgsql函数先检查information_schema然后执行动态查询重命名列呢?
  • 感谢您的提问。如何将需求发送给 PostgreSQL 构建者?

标签: postgresql ddl alter-table


【解决方案1】:

最好有两个函数,一个调用另一个:

CREATE OR REPLACE FUNCTION column_exists(ptable TEXT, pcolumn TEXT)
  RETURNS BOOLEAN AS $BODY$
DECLARE result bool;
BEGIN
    -- Does the requested column exist?
    SELECT COUNT(*) INTO result
    FROM information_schema.columns
    WHERE
      table_name = ptable and
      column_name = pcolumn;
    RETURN result;
END$BODY$
  LANGUAGE plpgsql VOLATILE;

CREATE OR REPLACE FUNCTION rename_column_if_exists(ptable TEXT, pcolumn TEXT, new_name TEXT)
  RETURNS VOID AS $BODY$
BEGIN
    -- Rename the column if it exists.
    IF column_exists(ptable, pcolumn) THEN
        EXECUTE FORMAT('ALTER TABLE %I RENAME COLUMN %I TO %I;',
            ptable, pcolumn, new_name);
    END IF;
END$BODY$
  LANGUAGE plpgsql VOLATILE;

【讨论】:

    【解决方案2】:

    @NessBird 2 函数方法很好,但 Column_Exists 函数可以简化为 select exists,避免计数,并作为 SQL 函数而不是 plpgsql 函数。

    create or replace function 
    column_exists(ptable text, pcolumn text, pschema text default 'public')
      returns boolean 
      language sql stable strict  
    as $body$
        -- does the requested table.column exist in schema?
        select exists 
             ( select null 
                 from information_schema.columns 
                 where table_name=ptable 
                   and column_name=pcolumn 
                   and table_schema=pschema
             ); 
    $body$;
    

    我添加了 schema 参数来处理具有相同表名的多个 schema。 除了可能添加架构外,rename_column_if_exists 保持不变。

    【讨论】:

    • 不需要退货单吗?
    • 没有。请参阅语言规范 - “SQL”。最后一条语句是 SQL 函数,必须是带有返回子句的 select 或 DML 语句。它返回语句的结果。想想看,我不认为 SQL 函数可以有回报 - 但不是 100% 肯定。
    【解决方案3】:

    请阅读article了解详细说明。

    DO $$
    BEGIN
      IF EXISTS(SELECT *
        FROM information_schema.columns
        WHERE table_name='your_table' and column_name='your_column')
      THEN
          ALTER TABLE "public"."your_table" RENAME COLUMN "your_column" TO "your_new_column";
      END IF;
    END $$;
    

    【讨论】:

    • 这很好,很整洁......也许我会把它写成一个函数并使用它。
    • @NessBird 如果您有解决方案,请您接受这个答案吗?所以其他人可以检查接受的答案并快速获得解决方案。
    【解决方案4】:

    您可以简单地处理anonymous code block 中可能引发的错误:

    DO
    $$
        BEGIN
            ALTER TABLE t
                RENAME COLUMN c1 TO c2;
        EXCEPTION
            WHEN undefined_column THEN RAISE NOTICE 'column t.c1 does not exist';
        END;
    $$;
    

    您可以省略 THEN 之后的文本以不执行任何操作:

    DO
    $$
        BEGIN
            ALTER TABLE t
                RENAME COLUMN c1 TO c2;
        EXCEPTION
            WHEN undefined_column THEN
         END;
    $$;
    

    当错误发生时,您可能只会得到一个数字。您可以从here 中找到条件名称(WHEN 之后的错误名称)。确保您使用的数据库版本正确。

    【讨论】:

      【解决方案5】:

      谢谢大家的好建议! 我已经为运行迁移编译了一个生产就绪的解决方案。我使用相同的函数来检查列是否存在,但我已将 rename_table_if_exists 函数替换为一个过程:

      CREATE OR REPLACE FUNCTION "product".column_exists (ptable text, pcolumn text, pschema text DEFAULT 'public')
          RETURNS boolean
          LANGUAGE sql
          STABLE STRICT
          AS $BODY$
          -- does the requested table.column exist in schema?
          SELECT
              EXISTS (
                  SELECT
                      NULL
                  FROM
                      information_schema.columns
                  WHERE
                      table_name = ptable
                      AND column_name = pcolumn
                      AND table_schema = pschema);
      $BODY$;
      
      
      CREATE OR REPLACE PROCEDURE "product".rename_column_if_exists (ptable TEXT, pcolumn TEXT, new_name TEXT, pschema text DEFAULT 'public')
      LANGUAGE plpgsql
      AS $$
      BEGIN
          -- Rename the column if it exists.
          IF product.column_exists (ptable,
              pcolumn,
              pschema) THEN
          EXECUTE FORMAT('ALTER TABLE %I.%I RENAME COLUMN %I TO %I;', pschema, ptable, pcolumn, new_name);
      END IF;
      END
      $$;
      

      可以这样使用:

      CALL <schema?>.rename_column_if_exists ('users', 'name', 'first_name', <schema name - optional>);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-26
        • 2011-01-05
        • 1970-01-01
        • 1970-01-01
        • 2023-01-27
        • 2011-04-29
        • 2021-03-08
        • 1970-01-01
        相关资源
        最近更新 更多