通过 Flyway SQL 脚本为 PostgreSQL 中新创建的列设置不为空

Posted

技术标签:

【中文标题】通过 Flyway SQL 脚本为 PostgreSQL 中新创建的列设置不为空【英文标题】:Set not null for a newly created column in PostgreSQL via Flyway SQL script 【发布时间】:2021-05-13 03:28:55 【问题描述】:

我有一个 Flyway 迁移脚本(在我们的 Spring Boot 应用程序中):

alter table bar
    add column foo_id int,
    add constraint fk_bar_foo_id foreign key (foo_id)
        references foo (id);

update bar
set foo_id = foo.id
from foo
where bar.foo_pid = foo.pid;

alter table bar
    alter column foo_id set not null;

它基本上是:添加一个新列,使其成为另一个表的 FK,使用来自另一个表的现有数据填充它,并设置非空约束。 当我启动由开发数据库支持的应用程序时,一切都按预期工作。 但是,应用程序在测试期间无法启动:

Migration V210111__foo_id.sql failed
----------------------------------------------------------
SQL State  : 23502
Error Code : 0
Message    : ERROR: column "foo_id" contains null values
Location   : db/migration/V210111__foo_id.sql
Line       : 11
Statement  : alter table bar
    alter column foo_id set not null

当我注释第 11 行(和第 12 行)时,脚本成功,当我查看测试数据库(嵌入式 postgres)时,我看到记录确实有 foo_id = null

# foo table
id  pid
0   00000000-0000-0000-0000-000000000000

# bar table
id  foo_id  foo_pid
0   <null>  0000000-0000-0000-0000-000000000000

注意事项:

    迁移脚本适用于某些数据库,但不适用于其他数据库。不知道哪个是哪个! 没有丢失数据。我可以在测试暂停时自己运行脚本的更新部分,一切正常。只是 Flyway 无法运行迁移。 同样,任何bar 行都不可能以空foo_id 结尾,因为foo.idfoo 表的PK,foo.pidfoo 表的PK(现在仍然是not null)。 foo_pid 也是 not nullfoo 表的 FK。 在此之前有很多迁移,添加新列、删除现有列、更改 PK 等。

我不确定可能是什么问题。也许它与 Flyway 运行迁移脚本的方式有关?原因是当我对开发数据库运行迁移时,只有一个迁移脚本要执行(这意味着数据库已经拥有一切,包括foo.idfoo.pid)。但是对于测试,Flyway 必须运行所有脚本(因为它是一个新的空数据库)。但据我所知,Flyway 在一个事务中运行每个脚本,因此在执行下一个脚本时数据应该是最终的。

有人知道问题可能是什么吗?有没有更多的解决方法?

更新 1

同样,任何 bar 行都不可能以 null 结尾...

相信 PG 而不是我的话是公平的(即使我多次查看架构)。为了消除对 null 值可能性的任何疑问,我将脚本的更新部分更改为:

--update bar
--set foo_id = foo.id
--from foo
--where bar.foo_pid = foo.pid;

update bar
set foo_id = 0;

它仍然失败并出现同样的错误????!

更新 2

有一个我认为不相关的触发器:

CREATE OR REPLACE FUNCTION public.update_modified_at()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
begin
    if row (NEW.*) is distinct from row (OLD.*) then
        NEW.modified_at = now();
        return NEW;
    else
        return OLD;
    end if;
end;
$function$
;

每个表都有一个modified_at 列:

create trigger tgr_update_modified_at before
update on bar for each row execute function update_modified_at();

【问题讨论】:

"同样,任何条形行都不可能以 null foo_id 结尾" - Postgres 证明你的假设是错误的。我猜你在 bar 中的行数不会比在 foo 中的行数多 一次一个步骤地进行,并验证每个步骤以查看哪个部分不起作用。然后您可以发布您的流程和代码的特定部分。 @Brad 我不确定我需要采取哪些步骤。如果我禁用第 11 行并检查数据,我会看到数据为空。当我尝试运行相同的更新查询时,它会填充数据。 @a_horse_with_no_name 请查看我的更新。 涉及到任何触发器? 【参考方案1】:

这部分可能不会做任何事情:

update bar
set foo_id = foo.id
from foo
where bar.foo_pid = foo.pid;

因为您刚刚在表格栏中添加了 foo_pid 列。 子句

where bar.foo_pid = foo.pid;   

正在将 null 与 foo.pid 进行比较,但总是失败,并且该语句不会更新任何内容。

【讨论】:

添加的是foo_id 而不是foo_pidfoo_pid 从一开始就在那里。正如我所说,该脚本适用于某些数据。另请参阅更新 #1。 啊啊啊啊。我需要新眼镜。我会删除这个答案。如果可以的话......

以上是关于通过 Flyway SQL 脚本为 PostgreSQL 中新创建的列设置不为空的主要内容,如果未能解决你的问题,请参考以下文章

如何创建脚本或 Flyway 可以配置为每次使用 SQL 回调调用它?

Flyway - 多个 git 分支上的 SQL 迁移脚本版本

flyway迁移后运行data.sql文件

序列和分区的版本化或可重复脚本 - Flyway?

Flyway 助力数据库脚本自动化管理攻略

Flyway 助力数据库脚本自动化管理攻略