问题描述
我正在尝试设计一种数据库结构,允许我将常见字段提取到一个名为“实体”的表中。
您可以将“实体”视为抽象类。每个“实体”都有一个所有者(一个引用的用户帐户)、一个创建时间和一些键值标签。
每个“实体”实际上要么是一个“对象”,要么是一个“视图”。从来没有。从来没有。
是否可以在 Postgresql 数据库(最新版本)上强制执行此约束? 如果没有,请告诉我并随时为我当前的架构提出更改建议。
我的初始化 sql 如下所示:
CREATE TABLE "account" (
"id" BIGSERIAL NOT NULL,"issuer" VARCHAR NOT NULL,"name" VARCHAR NOT NULL,PRIMARY KEY ("id"),UNIQUE ("issuer","name")
) ;
CREATE TABLE "entity" (
"id" BIGSERIAL NOT NULL,"owner_account_id" BIGINT NOT NULL,"creation_time" TIMESTAMP NOT NULL,FOREIGN KEY ("owner_account_id") REFERENCES "account" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
CREATE TABLE "entity_tag" (
"entity_id" BIGINT NOT NULL,"key" VARCHAR(100) NOT NULL,"value" VARCHAR(1000) NOT NULL,PRIMARY KEY ("entity_id","key"),FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
CREATE TABLE "object" (
"id" BIGSERIAL NOT NULL,"entity_id" BIGINT NOT NULL,"mime_type" VARCHAR NOT NULL,"size" BIGINT NOT NULL,FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
CREATE TABLE "view" (
"id" BIGSERIAL NOT NULL,"view_expression" JSON NOT NULL,FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
(当然,我可以而且我目前确实在我的应用程序中强制执行此操作。但是如果有一种方法也可以在数据库上强制执行此操作,我想这样做)
解决方法
没有简单的方法, 您必须在实体表中添加一列来指示实体的类型:
CREATE TYPE type_entity_class AS ENUM ('entity_tag','object','view');
在实体表中添加一列: entity_class type_entity_class,
在 entity_tag 表中添加列: entity_class type_entity_class 检查(entity_class = 'entity_tag')
在对象表中添加列: entity_class type_entity_class 检查(entity_class = 'object') ..
改变:
外键 ("entity_id","entity_class") REFERENCES "entity" ("id","entity_class")
,您可以在 PostgreSQL(PostgreSQL 有多棒)和 Oracle 中执行此操作,因为您需要一个支持可延迟约束的引擎,这是 SQL 标准的一个组成部分。
你可以这样做:
create table entity (
id int primary key not null,name varchar(20) not null,object_id int,view_id int,check (object_id is null and view_id is not null
or object_id is not null and view_id is null)
);
create table object (
id int primary key not null references entity (id),mime_type varchar(20) not null
);
create table view (
id int primary key not null references entity (id),view_expression varchar(50) not null
);
alter table entity add constraint c1
foreign key (object_id) references object (id) deferrable initially deferred;
alter table entity add constraint c2
foreign key (view_id) references view (id) deferrable initially deferred;
现在,您可以插入一个对象和一个视图:
begin transaction;
insert into entity (id,name,object_id,view_id)
values (10,'Object-10',10,null);
insert into object (id,mime_type) values (10,'image/png');
commit;
begin transaction;
insert into entity (id,view_id)
values (12,'View-12',null,12);
insert into view (id,view_expression) values (12,'a+b*c');
commit;
但是你不能插入抽象实体(没有具体的行):
begin transaction;
insert into entity (id,view_id)
values (14,'no-type-14',null);
commit; -- fails!
您也不能插入既是对象又是视图的实体:
begin transaction;
insert into entity (id,view_id) values (16,'Dual-16',16,16);
insert into object (id,mime_type) values (16,'text/plain');
insert into view (id,view_expression) values (16,'x*x');
commit; -- fails!
记住行的插入需要包含在事务中,以将约束检查推迟到事务结束。
请参阅 DB Fiddle 上的运行示例。