通过 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.id
是foo
表的PK,foo.pid
是foo
表的PK(现在仍然是not null
)。 foo_pid
也是 not null
和 foo
表的 FK。
在此之前有很多迁移,添加新列、删除现有列、更改 PK 等。
我不确定可能是什么问题。也许它与 Flyway 运行迁移脚本的方式有关?原因是当我对开发数据库运行迁移时,只有一个迁移脚本要执行(这意味着数据库已经拥有一切,包括foo.id
和foo.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_pid
。 foo_pid
从一开始就在那里。正如我所说,该脚本适用于某些数据。另请参阅更新 #1。
啊啊啊啊。我需要新眼镜。我会删除这个答案。如果可以的话......以上是关于通过 Flyway SQL 脚本为 PostgreSQL 中新创建的列设置不为空的主要内容,如果未能解决你的问题,请参考以下文章
如何创建脚本或 Flyway 可以配置为每次使用 SQL 回调调用它?