Sql 将不可为空的列添加到现有表失败。是",;“价值”;属性被忽略?

Sql 将不可为空的列添加到现有表失败。是",;“价值”;属性被忽略?,sql,postgresql,grails,database-design,liquibase,Sql,Postgresql,Grails,Database Design,Liquibase,背景:我们有一个Grails1.3.7应用程序,正在使用Liquibase来管理我们的数据库迁移 我正在尝试向现有表中添加一个非空的新列 我的变更集如下所示: changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") { addColumn(tableName: "layer") { column(name: "abstract_trimmed", type: "VARC

背景:我们有一个Grails1.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.exception.DatabaseException: Error executing SQL ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL: ERROR: column "abstract_trimmed" contains null values
在我看来,它没有使用“value”属性

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

Liquibase真的忽略了我的
属性吗,还是这里发生了一些我看不到的事情

我正在使用Grails1.3.7、数据库迁移插件1.0、Postgres9.0 如果在创建列时添加NOTNULL约束,“value”属性将不起作用(中未提及)。生成的SQL将无法执行

变通办法 问题中描述的解决方法是可行的。生成的SQL将是:

  • 添加列

    ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455);
    
  • 为每一行将其设置为非空值

    UPDATE table SET abstract_trimmed = 'No text';
    
  • 添加NOTNULL约束

    ALTER TABLE layer ALTER COLUMN abstract_trimmed SET NOT NULL;
    
  • 为什么? 列默认值仅插入带有
    INSERT
    的列中。“value”标记将为您完成此操作,但在添加列之后。Liquibase尝试一步添加列,并使用
    notnull
    约束:

    ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL;
    
    。。。当表格已经包含行时,不可能。它只是不够聪明

    替代解决方案 由于PostgreSQL 8.0(现在几乎永远如此),另一种选择是添加带有非空
    默认值的新列

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

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

    添加带有volatile
    默认值的列或更改
    现有列将要求保存整个表及其索引
    重写。作为例外,在更改现有文件的类型时
    列,如果
    USING
    子句未更改列内容和 旧类型是可强制为新类型的二进制类型或 新类型上的无约束域,不需要重写表; 但受影响列上的任何索引仍必须重建。桌子 和/或索引重建可能需要相当长的时间才能完成 大桌子;并且将暂时需要两倍的磁盘空间


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

    要通过两个步骤完成此操作:

  • 添加列,并将所有现有列上所需的值作为该列的默认值
  • 然后从列中删除defaultValue:

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

    感谢您提供的提示:varxhar(x)vs text with a constraint。我建议进行编辑以删除此项,因为它可能会分散对问题答案的注意力,但我将对此进行研究。干杯,@David:同意,
    text
    /
    varchar
    上的内容是一个旁白。我只想提一下,问题解决方案中提供的内容适用于MySQL,而不适用于MS SQL和,从这个问题很容易理解-Postgres.BTW为
    add column+not null
    生成错误SQL的问题是:)@KrzysztofKaczor:是的,谢谢你指出。我在备选解决方案中添加了
    notnull
    ,以匹配“变通方法”(有或没有)。但否:具有默认值的列不会“接受NULL并使用默认值”。与任何其他值一样,显式NULL会覆盖默认值(如果列定义为
    非NULL
    ,则会触发异常)。只有在未向列写入任何内容(即,
    插入
    (显式或隐式)中根本未提及该列,或者使用了
    默认
    关键字时,才应用列默认值。不幸的是,这不仅指定了在运行数据库迁移时要向列中插入的值,但在以后添加行时,如果没有给定值,则插入哪个值。这可能不是你想要的。
    ALTER TABLE layer
    ADD COLUMN abstract_trimmed varchar(455) NOT NULL DEFAULT 'No text';
    
     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")
        }