确保一组表中只有一个外键引用
Ensure there's exactly one foreign key reference from a set of tables
我正在尝试设计一种数据库结构,允许我将公共字段提取到一个名为“实体”的 table 中。
您可以将“实体”视为抽象的class。每个“实体”都有一个所有者(一个引用的用户帐户)、一个创建时间和一些键值标签。
每个“实体”实际上要么是“对象”,要么是“视图”。从来没有。从来没有 none 个。
是否可以在 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,
PRIMARY KEY ("id"),
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,
PRIMARY KEY ("id"),
FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
CREATE TABLE "view" (
"id" BIGSERIAL NOT NULL,
"entity_id" BIGINT NOT NULL,
"view_expression" JSON NOT NULL,
PRIMARY KEY ("id"),
FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
(当然,我可以而且我目前确实在我的应用程序中强制执行此操作。但是如果也有一种方法可以在数据库上强制执行此操作,我想这样做)
没有简单的方法,
你必须在实体table:
中添加一列来表示实体的类型
创建类型 type_entity_class 作为枚举 ('entity_tag', 'object', 'view');
在实体中添加一列 table:
entity_class type_entity_class,
在entity_tagtable中添加列:
entity_class type_entity_class 检查 (entity_class = 'entity_tag')
在对象 table 中添加列:
entity_class type_entity_class 检查 (entity_class = 'object')
..
变化:
外键(“entity_id”,“entity_class”)引用“实体”(“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, name, object_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, name, object_id, view_id)
values (14, 'no-type-14', null, null);
commit; -- fails!
您也不能插入既是对象又是视图的实体:
begin transaction;
insert into entity (id, name, object_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 中的 运行 示例。
我正在尝试设计一种数据库结构,允许我将公共字段提取到一个名为“实体”的 table 中。
您可以将“实体”视为抽象的class。每个“实体”都有一个所有者(一个引用的用户帐户)、一个创建时间和一些键值标签。
每个“实体”实际上要么是“对象”,要么是“视图”。从来没有。从来没有 none 个。
是否可以在 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,
PRIMARY KEY ("id"),
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,
PRIMARY KEY ("id"),
FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
CREATE TABLE "view" (
"id" BIGSERIAL NOT NULL,
"entity_id" BIGINT NOT NULL,
"view_expression" JSON NOT NULL,
PRIMARY KEY ("id"),
FOREIGN KEY ("entity_id") REFERENCES "entity" ("id") ON DELETE CASCADE ON UPDATE CASCADE
) ;
(当然,我可以而且我目前确实在我的应用程序中强制执行此操作。但是如果也有一种方法可以在数据库上强制执行此操作,我想这样做)
没有简单的方法, 你必须在实体table:
中添加一列来表示实体的类型创建类型 type_entity_class 作为枚举 ('entity_tag', 'object', 'view');
在实体中添加一列 table: entity_class type_entity_class,
在entity_tagtable中添加列: entity_class type_entity_class 检查 (entity_class = 'entity_tag')
在对象 table 中添加列: entity_class type_entity_class 检查 (entity_class = 'object') ..
变化:
外键(“entity_id”,“entity_class”)引用“实体”(“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, name, object_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, name, object_id, view_id)
values (14, 'no-type-14', null, null);
commit; -- fails!
您也不能插入既是对象又是视图的实体:
begin transaction;
insert into entity (id, name, object_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 中的 运行 示例。