如何处理 1 --> 0..1 关系?

Posted

技术标签:

【中文标题】如何处理 1 --> 0..1 关系?【英文标题】:What to do with a 1 --> 0..1 relationship? 【发布时间】:2013-04-15 08:58:25 【问题描述】:

我们有一个Claim 实体,可以通过EmailVerification 证明,将MetaCode 放入相关网站或上传CertificateFile。现在的问题是,索赔人可以选择使用上述方法的任意组合来证明他们的索赔,但在每个类别中他们只获得了一次机会。含义:

 Claim -- 0..1 --> EmialVerification
 Claim -- 0..1 --> MetaCode
 Claim -- 0..1 --> CertificateFile

所以这是有效的:

 Claim #20 --> EmialVerification #12
 Claim #20 --> MetaCode (none)
 Claim #20 --> CertificateFile #124

还有这个:

 Claim #34 --> EmialVerification (none)
 Claim #34 --> MetaCode (none)
 Claim #34 --> CertificateFile (none)

不是这个:

 Claim #34 --> EmialVerification #1034 & #450
 Claim #34 --> MetaCode (none)
 Claim #34 --> CertificateFile (none)

因为#43 与两个EmailVerifications 相关。

现在我被困在表模式中,因为我不知道如何最好地建模 0..1 关系:

四个单独的表,带有人工 PK(整数 id),并像往常一样使用 1..* 关系 四个单独的表,但只有一个有 id,另外 3 个使用此 id 作为它们自己的 PK 和 FK 到 Claim 表? 只需将其他 3 个表聚合到 Claim 表中,并使用 Null 指示缺乏关系? 还有什么?

编辑 看来问题不是很清楚:Claim 中的一行可以连接到 0(根本未连接)或其他 3 行中的任何一行,但 Claim 中的任何单行都可以连接到任何其他 3 个表中的多于一行(如示例中的第 3 种情况)。

编辑一个可怕的错字潜伏在阴影中(Claim #34 在每个示例的第一行被错误地输入为Claim #43)!!! .真对不起。顺便说一句,我认为正确的答案仍然成立。

【问题讨论】:

相关***.com/q/517417/369489 那么Claim表需要在EmailVerification、MetaCode、CertificateFile这一个且只有一个表中有对应的行吗? 那么电子邮件、元代码和证书是否必须相互排斥?因此,如果声明 #1 是通过电子邮件提出的,那么元代码和证书都不能将声明 #1 作为外键? 如果它们相互排斥,我们是否可以创建一个 1 到 0..1 的单个“已验证”表,将声明 ID 作为唯一 FK 并使用枚举字段来标识已验证的方法? @KacireeSoftware 它们不是相互排斥的(示例中的第一种情况),但如果 Claim 连接到 MetaCode 例如,那么它只能连接到 @987654343 中的一行@ 但同时可以连接到其他 2 个表中的任一行。 【参考方案1】:

理清了cmets之后,你似乎只需要在各个验证表中存储主键即可。 (您不必使用人工或代理键。如果“claim_num”是自然键,并且它是 varchar(15),请使用“claim_num”。)

create table Claims (
  claim_id integer primary key,
  other_columns_go_here char(1) not null default 'x'
);

create table EmailVerifications (
  claim_id integer primary key references Claims (claim_id),
  email_verification_num integer not null unique,
  other_columns_go_here char(1) not null default 'x'
);

create table MetaCodes (
  claim_id integer primary key references Claims (claim_id),
  metacode_num integer not null unique,
  other_columns_go_here char(1) not null default 'x'
);

create table CertificateFiles (
  claim_id integer primary key references Claims (claim_id),
  certificate_file_num integer not null unique,
  other_columns_go_here char(1) not null default 'x'
);

这些插入将成功。

insert into Claims values (20);
insert into EmailVerifications values (20, 12);
insert into CertificateFiles values (20, 124);

前两次插入将成功。 “EmailVerifications”上的 PRIMARY KEY 约束将使第三个失败。

insert into Claims values (43);
insert into EmailVerifications values (43, 1034);
insert into EmailVerifications values (43, 450);

以下内容与明确要求不符。认为它的持续存在是一种奖励。

如果我理解正确,您希望声明中的每一行都被零行或仅一行引用。

create table Claims (
  claim_id integer primary key,
  verification_code char(1) not null
    check (verification_code in ('c', 'e', 'm')),
  unique (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

create table EmailVerifications (
  claim_id integer not null,
  verification_code char(1) not null default 'e'
    check (verification_code = 'e'),
  primary key (claim_id, verification_code),
  foreign key (claim_id, verification_code)
    references Claims (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

create table MetaCodes (
  claim_id integer not null,
  verification_code char(1) not null default 'm'
    check (verification_code = 'm'),
  primary key (claim_id, verification_code),
  foreign key (claim_id, verification_code)
    references Claims (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

create table CertificateFiles (
  claim_id integer not null,
  verification_code char(1) not null default 'c'
    check (verification_code = 'c'),
  primary key (claim_id, verification_code),
  foreign key (claim_id, verification_code)
    references Claims (claim_id, verification_code),
  other_columns_go_here char(1) not null default 'x'
);

begin;
  insert into Claims values (1, 'c', 'x');
  insert into CertificateFiles values (1, 'c', 'x');
commit;

begin;
  insert into Claims values (2, 'e', 'x');
  insert into EmailVerifications values (2, 'e', 'x');
commit;

begin;
  insert into Claims values (3, 'm', 'x');
  insert into MetaCodes values (3, 'm', 'x');
commit;

“声明”中的重叠 PRIMARY KEY 和 UNIQUE 约束是必需的。其他表引用“claim_id”和“verification_code”这对列,除非这对列有 UNIQUE 约束,否则它们不能这样做。

verification_code 及其 CHECK 约束保证外键引用来自正确的表。

-- This insert will fail. 
-- Inserting into EmailVerifications requires 'e', not 'c'.
begin;
  insert into Claims values (4, 'c', 'x');
  insert into EmailVerifications values (4, 'c', 'x');
commit;

-- This insert will fail. (Duplicate row.)
insert into EmailVerifications values (2, 'e', 'x');

-- This insert will fail. (Trying to make two rows reference one row in Claims.)
insert into CertificateFiles values (2, 'e', 'x');

【讨论】:

+1 在证明表中巧妙地使用 PK 来施加 ..1 约束。是否可以向 RDBMS 发出信号(假设有问题的特定服务器支持这一点)证明表中的 PK 同时实际上是对 Claim 的 FK? @ashy_32bit:当然。我一开始就应该这样做。我对今天和明天的工作面试考虑得太周到了。更新代码。 请您根据第二次编辑重新阅读问题:-D? 我早些时候发现了这种差异,但在我的测试中使用了 43。我的答案(最上面的那个)仍然成立。

以上是关于如何处理 1 --> 0..1 关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 REST api 中的关系

如何处理 JSON 中的多对多关系?

如何处理与 RestKit 的关系同步(离线支持)

核心数据如何处理基于 ID 的关系

如何处理 Flux 应用程序中的关系?

MyBatis学习笔记 —— 字段名和属性不一致的情况下,如何处理映射关系