背景:我们有一个 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")
}
}
}
应该将值“无文本”插入到每个现有行中,因此满足非空约束。 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
如果您在创建列时添加非空约束,“value”属性将不起作用(文档中未提及)。生成的SQL将无法执行。
问题中描述的解决方法是可行的方法。生成的 SQL 将是:
添加列
ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455);
将每行设置为非空值
UPDATE table SET abstract_trimmed = 'No text';
添加 NOT NULL 约束
ALTER TABLE layer ALTER COLUMN abstract_trimmed SET NOT NULL;
列默认值仅插入到带有
INSERT
的列中。 “value”标签将为您执行此操作,但在after添加列。 Liquibase 尝试一步添加列,并设置 NOT NULL
约束:
ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL;
...当表已包含行时,这是“不可能的”。它只是不够聪明。 替代解决方案
DEFAULT
:
ALTER TABLE layer
ADD COLUMN abstract_trimmed varchar(455) NOT NULL DEFAULT 'No text';
说明书:当添加了 ADD COLUMN
和非易失性
的色谱柱时, 指定,默认值在声明时计算,并且 结果存储在表的元数据中。该值将用于 所有现有行的列。如果未指定DEFAULT
,则为 NULL 用过的。在这两种情况下都不需要重写表格。DEFAULT
添加具有 volatileDEFAULT
的列或更改 现有列将需要整个表及其索引 重写。作为例外,当更改现有的类型时 列,如果
子句不更改列内容并且 旧类型要么是新类型的二进制强制转换,要么是 新类型上不受约束的域,不需要重写表; 但受影响列上的任何索引仍必须重建。桌子 和/或索引重建可能需要大量时间 大桌子;并且暂时需要两倍的磁盘空间。USING
在所有现有列上添加具有所需值的列,作为该列的默认值。
changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") {
addColumn(tableName: "layer") {
column(name: "abstract_trimmed", type: "VARCHAR(455)", defaultValue: "No text") {
constraints(nullable: "false")
}
}
}
changeSet(author: "someCoolGuy (generated)", id: "1326842592275-2") {
dropDefaultValue(tableName: "layer" columnName: "abstract_trimmed")
}
这样做的一个优点是,如果您有一个非静态值作为您想要输入到列中的值,它会计算一次并使用它来填充所有现有行(例如:时间戳),而不是可能重新计算它每行更改。
<changeSet id="Add NEW_COLUMN to MY_TABLE table" author="Author">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="MY_TABLE" columnName="NEW_COPUMN"/>
</not>
</preConditions>
<addColumn tableName="MY_TABLE">
<column name="NEW_COLUMN" type="text" defaultValue="SOMETHING">
<constraints nullable="false"/>
</column>
</addColumn>
<dropDefaultValue tableName="MY_TABLE" columnName="NEW_COLUMN"/>
</changeSet>
我只是创建一个具有默认值的列,然后在同一变更集中将其删除。
希望这对某人有用