sql - 将不可为空的列添加到现有表失败。 "value"属性是否被忽略?

标签 sql postgresql grails database-design liquibase

背景:我们有一个 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”插入到每个现有行中,因此满足非空约束。 Liquibase "Add Column" docs .

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

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

在我看来它没有使用“值”属性。

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

    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

最佳答案

简答

如果您在创建列时添加非空约束,“值”属性将不起作用(documentation 中未提及)。生成的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 的列中。 “值”标记将为您执行此操作,但 添加该列之后。 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';

The manual:

When a column is added with ADD COLUMN and a non-volatile DEFAULT is specified, the default is evaluated at the time of the statement and the result stored in the table's metadata. That value will be used for the column for all existing rows. If no DEFAULT is specified, NULL is used. In neither case is a rewrite of the table required.

Adding a column with a volatile DEFAULT or changing the type of an existing column will require the entire table and its indexes to be rewritten. As an exception, when changing the type of an existing column, if the USING clause does not change the column contents and the old type is either binary coercible to the new type or an unconstrained domain over the new type, a table rewrite is not needed; but any indexes on the affected columns must still be rebuilt. Table and/or index rebuilds may take a significant amount of time for a large table; and will temporarily require as much as double the disk space.

关于sql - 将不可为空的列添加到现有表失败。 "value"属性是否被忽略?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8904316/

相关文章:

java - 显示我的数据库中的名称时遇到问题

sql - 如何在 SQL 中检查 USER 是否已在数据库中创建?

sql - 在 Postgres 的单个索引中包含多个列

SQL连接表及限制条件匹配结果

grails - Ra-在 Grails 应用程序中对更改后的用户进行身份验证

grails - 隐式依赖json-lib-2.4-jdk15.jar会导致构建失败,即使在build.gradle中已将其排除在外

sql - SUM()函数sql有上限

java - 使用 JDBC 从 postgresql 获取消息响应

sql - 我如何优化这个缓慢的 PostgreSQL 查询

Java:获取下载的附件的文件名(HttpClient,PostMethod)