sql - 通过 Flyway SQL 脚本为 PostgreSQL 中新创建的列设置不为空
问题描述
我有一个 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
行都不可能以 null 结尾foo_id
,因为,foo.id
是表的 PKfoo
,foo.pid
是表的 PKfoo
(并且仍然是not null
)。也是foo_pid
和not null
一个 FKfoo
表。 - 在此之前有很多迁移,添加新列,删除现有列,更改 PK 等。
我不确定可能是什么问题。也许它与 Flyway 运行迁移脚本的方式有关?原因是当我对开发数据库运行迁移时,只有一个迁移脚本可以执行(这意味着数据库已经拥有一切,包括foo.id
and 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();
解决方案
这部分可能不会做任何事情:
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 进行比较总是失败,并且该语句不会更新任何内容。
推荐阅读
- elasticsearch - Elasticsearch:使用动态映射过滤时的问题
- c++ - v8 源码中 ArrayMap 函数的 callbackfn 参数是什么?
- linkedin - 如何获得“其他会员资料”?
- spring-boot - Spring Boot V2.1.9.RELEASE 不支持 Spring kafka 2.3.1
- django - Django ForeignKey 反向查询
- wpf - 如何正确实现 XAML INotifyPropertyChanged 以防止 GDI 泄漏
- gitlab - Gitlab shell runner 无法上传大于 63KB 的工件:“501 未实现”
- python - 应用 minmaxscaler 后,列中的 Pandas 科学形式值给出错误的输出
- r - 如何通过第二个 col 中的最大值找到第一个 col 值(在矩阵中)
- python - 将文本字符串拆分为 int 和 text 变量