向现有表中添加不可为null的列失败.是“;值“;属性被忽略



背景:我们有一个Grails 1.3.7应用程序,正在使用Liquibase来管理我们的数据库迁移。

我正在尝试将一个新列添加到一个不为空的现有表中。

我的变更集是这样的:

    changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") {
        addColumn(tableName: "layer") {
            column(name: "abstract_trimmed", type: "VARCHAR(455)", value: "No text") {
                constraints(nullable: "false")
            }
        }
    }

它应该在每个现有行中插入值"无文本",因此满足非null约束。Liquibase"添加列"文档。

但是当应用迁移变更集时,我会得到以下异常:

liquibase.exception.DatabaseException: Error executing SQL ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL: ERROR: column "abstract_trimmed" contains null values

在我看来,它似乎没有使用"value"属性。

如果我把我的变更集改为如下工作,我也可以实现同样的目标。但我不想(也不应该)这样做。

    changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") {
        addColumn(tableName: "layer") {
            column(name: "abstract_trimmed", type: "VARCHAR(455)")
        }
        addNotNullConstraint(tableName: "layer", columnName:"abstract_trimmed", defaultNullValue: "No text")
    }

Liquibase真的忽略了我的value属性吗?还是这里有其他我看不到的东西?

我使用的是Grails 1.3.7,数据库迁移插件1.0,Postgres 9.0

简短回答

如果在创建列时添加了一个非null约束,则"value"属性将不起作用(文档中没有提到这一点)。生成的SQL将无法执行。

变通办法

问题中描述的解决方法是可行的。结果SQL将是:

  1. 添加列

    ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455);
    
  2. 为每行将其设置为非零值

    UPDATE table SET abstract_trimmed = 'No text';
    
  3. 添加NOT NULL约束

    ALTER TABLE layer ALTER COLUMN abstract_trimmed SET NOT NULL;
    

为什么

列默认值仅插入到具有INSERT的列中。"value"标记将为您执行此操作,但之后会添加该列。Liquibase尝试在NOT NULL约束到位的情况下一步添加列:

ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL;

当表已经包含行时,这是不可能的。它只是不够聪明。

替代解决方案

由于PostgreSQL 8.0(现在几乎永远如此),另一种选择是添加带有非空DEFAULT的新列:

ALTER TABLE layer
ADD COLUMN abstract_trimmed varchar(455) NOT NULL DEFAULT 'No text';

手册:

当列添加有ADD COLUMN并且非易失性DEFAULT指定,则在语句时评估默认值,并且存储在表的元数据中的结果。该值将用于所有现有行的列。如果未指定DEFAULT,则NULL为习惯于在这两种情况下都不需要重写表。

添加具有易失性DEFAULT的列或更改现有列将要求整个表及其索引重写。作为例外,当更改现有列,如果USING子句不更改列内容,并且旧类型对新类型是二进制可强制的,或者是不受约束的域超过了新类型,不需要重写表;但是仍然必须重建受影响列上的任何索引。桌子和/或索引重建可能需要相当长的时间大桌子;并且将临时需要多达两倍的磁盘空间。

使用"defaultValue"而不是"value"为新列设置默认值。

分两步完成:

  1. 添加具有所有现有列所需值的列作为该列的defaultValue
 changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") {
        addColumn(tableName: "layer") {
            column(name: "abstract_trimmed", type: "VARCHAR(455)", defaultValue: "No text") {
                constraints(nullable: "false")
            }
        }
    }
  1. 然后从列中删除defaultValue:
 changeSet(author: "someCoolGuy (generated)", id: "1326842592275-2") {
        dropDefaultValue(tableName: "layer" columnName: "abstract_trimmed")
    }

这样做的一个优点是,如果您有一个非静态值作为您想要输入到列中的值,它会计算一次,并使用它来填充所有现有行(例如:时间戳),而不可能为每一行的更改重新计算它。

相关内容

  • 没有找到相关文章

最新更新