向现有表添加不可空列失败。 “value”属性是否被忽略?

问题描述 投票:0回答:4

背景:我们有一个 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

sql postgresql grails database-design liquibase
4个回答
32
投票

简短回答

如果您在创建列时添加非空约束,“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”标签将为您执行此操作,但在after添加列。 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 用过的。在这两种情况下都不需要重写表格。
  
  
添加具有 volatile

DEFAULT

的列或更改 现有列将需要整个表及其索引 重写。作为例外,当更改现有的类型时 列,如果

USING
子句不更改列内容并且 旧类型要么是新类型的二进制强制转换,要么是 新类型上不受约束的域,不需要重写表; 但受影响列上的任何索引仍必须重建。桌子 和/或索引重建可能需要大量时间 大桌子;并且暂时需要两倍的磁盘空间。

    


2
投票


2
投票

在所有现有列上添加具有所需值的列,作为该列的默认值。
  1. changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") { addColumn(tableName: "layer") { column(name: "abstract_trimmed", type: "VARCHAR(455)", defaultValue: "No text") { constraints(nullable: "false") } } }
然后从列中删除默认值:
  1. changeSet(author: "someCoolGuy (generated)", id: "1326842592275-2") { dropDefaultValue(tableName: "layer" columnName: "abstract_trimmed") }
这样做的一个优点是,如果您有一个非静态值作为您想要输入到列中的值,它会计算一次并使用它来填充所有现有行(例如:时间戳),而不是可能重新计算它每行更改。


0
投票

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

我只是创建一个具有默认值的列,然后在同一变更集中将其删除。

希望这对某人有用

© www.soinside.com 2019 - 2024. All rights reserved.