背景:我们有一个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") } } }
哪个应该在每个现有行中插入值“ No text”,从而满足not 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属性,还是这里发生了其他我看不见的事情?
value
我正在使用Grails 1.3.7,数据库迁移插件1.0,Postgres 9.0
如果在创建列时添加了非null约束,则“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。“值”标签将为您完成此操作,但是 在 添加列 之后 。Liquibase尝试一步一步添加列,并设置NOT NULL约束:
INSERT
NOT NULL
ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL;
…当表已经包含行时,这是 不可能 的。只是不够聪明。
从PostgreSQL 8.0开始(到现在为止几乎永远如此),一种替代方法是添加 一个带有非nullDEFAULT的新列:
DEFAULT
ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455) NOT NULL DEFAULT 'No text';
手册:
当添加了列ADD COLUMN并DEFAULT指定了非易失性时,将在声明时评估默认值,并将结果存储在表的元数据中。该值将用于所有现有行的列。如果未DEFAULT指定,则使用NULL。在任何情况下都不需要重写该表。 添加具有volatile的列DEFAULT或更改现有列的类型将需要重写整个表及其索引。例外情况是,在更改现有列的类型时,如果该USING子句不更改列的内容,并且旧类型可以强制转换为新类型的二进制或新类型的不受约束的域,则无需重写表;否则,无需重写表。但是受影响的列上的所有索引仍必须重建。对于大型表,表和/或索引的重建可能会花费大量的时间。并且暂时需要多达两倍的磁盘空间。
当添加了列ADD COLUMN并DEFAULT指定了非易失性时,将在声明时评估默认值,并将结果存储在表的元数据中。该值将用于所有现有行的列。如果未DEFAULT指定,则使用NULL。在任何情况下都不需要重写该表。
ADD COLUMN
添加具有volatile的列DEFAULT或更改现有列的类型将需要重写整个表及其索引。例外情况是,在更改现有列的类型时,如果该USING子句不更改列的内容,并且旧类型可以强制转换为新类型的二进制或新类型的不受约束的域,则无需重写表;否则,无需重写表。但是受影响的列上的所有索引仍必须重建。对于大型表,表和/或索引的重建可能会花费大量的时间。并且暂时需要多达两倍的磁盘空间。
USING