我在这里需要多态关联还是我的模型不够充实?
Do I need a polymorphic association here or is my model not fleshed out enough?
我来这里是为了提出一个问题,过去几天我在开发数据库设计时一直在努力解决这个问题。
简介:
该数据库旨在存储有关电机类型的信息(不是关于实际电机,而是更多关于它们的设计、参数等)、它们的 components/attributes 以及某些电机类型之间的关系(每个电机类型都与所谓的基本电机类型相关) .所以我们有几个电机类型,每个电机类型都有 many 属性(我称它们为 detailtypes)。每个细节类型依次可以出现在 一个或多个 电机类型中。每个 motortype:detailtype 组合应该(恰好)有一个值。
我需要能够比较两种不同电机类型的特定细节类型的值,并根据此比较存储信息。 (存储此信息是数据库的中心目的。)
现在第一个也是最简单的方法是制作一个 motortype table 并使其每个 detailtype 都有一列,这样我就可以为每个组合存储一个值:
Approach #1:
CREATE TABLE Motortype(
id INTEGER PRIMARY KEY,
description CHAR (50),
detailtype1 CHAR (50),
detailtype2 INTEGER,
detailtype3 YES/NO,
detailtype4 DOUBLE );
大约有一百个(这个数字将来可能会慢慢增长)这样的细节类型要输入到数据库中。添加更多对应于添加更多列(可管理),更改它们将导致重命名列(坏主意?)。当我想将电机类型与它们的 detailtype2 值上的 ID 2 和 5 进行比较并保存有关此比较的信息时,就会出现问题。我不知道怎么办。 (此外,属性不能具有 NOT NULL 约束,因为在创建条目时某些数据可能只是未知的...)
这就是我继续使用方法 2 的原因,它似乎以某种方式实现了 EAV。
为了允许存储比较信息,我将 detailtypes 从属性移动到 mototype 到它们自己的实体,同时将不需要比较的那些保留为属性:
Approach #2:
CREATE TABLE Motortype(
id INTEGER PRIMARY KEY,
description CHAR (50),
notComparableAttribute1 CHAR (50),
notComparableAttribute2 INTEGER
);
CREATE TABLE Detailtype(
id INTEGER PRIMARY KEY,
description CHAR (50),
datatype CHAR (3)
);
CREATE TABLE MotorDetail(
id INTEGER PRIMARY KEY,
motortype_id INTEGER CONSTRAINT fk_mt_id REFERENCES Motortype(id) NOT NULL,
detailtype_id INTEGER CONSTRAINT fk_dt_id REFERENCES Detailtype(id) NOT NULL,
value CHAR (50) NOT NULL
);
然后,此配置允许我引用 MotorDetail 中的任何特定值对并保存此比较的附加信息。 (请注意,此比较有更多维度 - 它与 MotorDetail 没有 1:1 关系。而是 1:n MotorDetail:ComparisonData 关系。否则我可以直接存储所有比较信息在 MotorDetail 中。)
然而,事实上我现在有一个需要保存多种不同数据类型(字符串、整数、浮点数、布尔值)的值属性,这意味着输入验证的责任从数据库转移到了我身上(我需要确保用户只能以对相应 Detailtype 有意义的方式输入数据)。这完全没问题 - 只是多了一点编程工作,但我觉得它可以用更优雅的方式解决。
所以在对多态关联进行一些研究之后,我想到了下一个方法(Motortype 和 Detailtype 与 #2 中的一样):
Approach #3:
CREATE TABLE MotorDetail(
id INTEGER PRIMARY KEY,
motortype_id INTEGER CONSTRAINT fk_mt_id REFERENCES Motortype(id) NOT NULL,
detailtype_id INTEGER CONSTRAINT fk_dt_id REFERENCES Detailtype(id) NOT NULL,
valueTxt CHAR (50),
valueInt INTEGER,
valueDbl DOUBLE,
valueBoo YES/NO
);
值属性不能有 NOT NULL 约束,因为 75% 的值将为空(根据要求,每个 MotorDetail 只能有一个值)。只要我只需要特定数据类型的值,查询值似乎就可以,否则它会变得更加复杂(例如,将它们显示在表单上)。或者我应该说 乏味 而不是复杂。
因此,在最后一次尝试解决这个问题时,我提出了我的最终方法(同样是#2 中的 Motortype 和 Detailtype):
Approach #4:
CREATE TABLE MotorDetail(
id INTEGER PRIMARY KEY,
motortype_id INTEGER CONSTRAINT fk_mt_id REFERENCES Motortype(id) NOT NULL,
detailtype_id INTEGER CONSTRAINT fk_dt_id REFERENCES Detailtype(id) NOT NULL
);
CREATE TABLE MotorDetailValueTxt(
id INTEGER PRIMARY KEY,
valueTxt CHAR (50)
);
CREATE TABLE MotorDetailValueInt(
id INTEGER PRIMARY KEY,
valueTxt INTEGER
);
... (same for MotorDetailValueDbl and MotorDetailValueBoo)
我不确定这个。不确定如何表示值 tables 中的每个 id 都与一个 MotorDetail(id) 绑定并且需要在 all 值 tables。甚至不确定我是否完全理解了基本概念。但我可以肯定的是,这样做数据库无法强制执行参照完整性。这就是为什么我不打算使用它。在测试数据库中,我能够使用 UNION 生成查询以获取所有不同的值,但我认为我不想在 table 条目级别进行任何内务处理(以某种方式确保没有死条目) .
问题:
鉴于目前概述的详细信息,是否有可能的解决方案不涉及某种编码魔法(VBA 或其他)?一个优雅的解决方案,以某种方式允许在数据库级别处理这一切(tables+关系,而不是报告、表格等...)?由于到目前为止我提出的所有内容似乎都很笨拙。
注意:我是数据库设计和一般数据库的新手。我已经接受了 3 天的 MS Access 培训,但这主要是为了学习 GUI 和构建一些非常基本的数据库而量身定制的。关于数据库开发,我知道(或假设知道)的任何其他事情都是通过阅读博客了解到的——最重要的是:关于 SO 的问题和答案。
可以肯定的是,我没有(完全)理解我所讨论的一个或多个概念,因此歪曲了其中的一些概念。如果是这样,或者如果有任何遗漏,请在评论中向我指出或进行编辑以改进我的问题(并允许我阅读它)。 :)
编辑:进行了一次编辑,而且是一个很大的编辑。我试图添加信息,澄清那里的内容并仍然保持问题的原始特征和意图。希望我至少部分成功了。 :)
你的问题看起来比你的设计比较简单和基础。对于您的应用程序的给定片段,这似乎比必要或期望的更复杂。
业务关系(船舶)/协会由 table 表示。我们可以用一个predicate——一个列参数化的语句模板来表征一个关系(ship)/association/table。 table 包含产生真实 命题 --陈述的行。
你没有给出比以下更复杂的设计的任何理由:
-- motor motorID has (non-details) ...
Motor(motorID, ...)
-- motor motorID has NumOfCylinders cylinders
MotorNumOfCylinders(motorId, NumOfCylinders)
-- motor motorID has cylinder material CylinderMaterial
MotorCylinderMaterial(motorId, CylinderMaterial)
...
你只想要一台电机吗table?
-- motor motorID has (non-details) ...
-- AND motor motorID has NumOfCylinders cylinders
-- AND ( NumOfCylinders > 0 and motor motorID has cylinder material CylinderMaterial ...
-- OR NumOfCylinders = 0 and CylinderMaterial = NULL ...
-- )
...
-- PK (motorID)
Motor(motorId, ..., NumOfCylinders, CylinderMaterial, ...)
您还没有解释随着时间的推移会发生什么变化。您想要更改字符串与给定详细信息的关联吗?对于可选字符串:
-- detail "NumOfCylinders" is written NumOfCylinders
NumOfCylindersString(NumOfCylinders)
-- detail "CylinderMaterial" is written CylinderMaterial
CylinderMaterialString(CylinderMaterial)
...
您只想要一个这样的 table 吗?对于必填字符串:
-- detail "NumOfCylinders" is written NumOfCylinders
-- AND detail "CylinderMaterial" is written CylinderMaterial
...
DetailStrings(NumOfCylinders, CylinderMaterial, ...)
您是否明确想要不同的 kinds/types 电机,以便在处理特定 kind/type 时具有静态约束?
-- motor motorID ... and has NumOfCylinders cylinders and weighs Weight kg and ...
Motor(motorId, ..., NumOfCylinders, Weight, ...)
-- piston motor motorID has cylinder material CylinderMaterial ...
PistonMotor(motorId, CylinderMaterial, ...)
-- electric motor motorId has ...
-- and it is isAC that it takes AC current
-- and it is isDC that it takes DC current
...
ElectricMotor(motorId, isAC, isDC, ...)
...
是否要以冗余和计算为代价将电机限制为恰好 kind/type?
-- motor motorId is of type motorType and ...
Motor(motorId, motorType, ...)
-- PistonMotor enforce (motorId, 'piston') in (select motorId, motorType from Motor)
-- ElectricMotor enforce (motorId, 'electric') in (select motorId, motorType from Motor)
...
是否要根据不同的成本权衡(通过存储或生成的列值)以声明方式进行约束?
-- motor motorID has type motorType and motorType = 'piston' and cylinder material CylinderMaterial ...
-- FK (motorID, motorType) references Motor (motorID, motorType)
-- check (motorType = 'piston')
PistonMotor(motorId, motorType, CylinderMaterial, ...)
-- motor motorID has type motorType and motorType = 'electric' and ...
-- FK (motorID, motorType) references Motor (motorID, motorType)
-- check (motorType = 'electric')
ElectricMotor(motorId, motorType, isAC, isDC, ...)
...
更多?
-- Motor check (NOT (motorType = 'electric' AND NumOfCylinders <> 0))
随着时间的推移,一个细节可以有不同的值吗?你想随着时间的推移有不同的细节吗?您可以使用 DDL 来实现更改。您想知道当前有哪些详细信息或详细信息值吗?查询元数据。或者可能有一些非系统 table 或其枢轴 1:1 与系统 table 或其枢轴的行。
您是否以某种方式 think/assume/suspect/fear 以牺牲复杂性和处理为代价,通过 DML 实现用户控制的状态更改比通过 DDL 更好? DDL 更新实施是否被证明太慢?您可以通过 EAV 及其伴随的优缺点对这些数据库状态进行部分或全部编码。
An EAV 'database' [sic] is literally mathematically straightforwardly an undocumented description in triples of a database and its metadata, with no functionality to
To paraphrase Greenspun, any sufficiently complex EAV project contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of a DBMS.
(请注意,在 EAV 中,一列给出了另一列的类型——其中数据对于表示是必不可少的——并且在一些子类型习语中——作为冗余的变体标签并用于约束。)
鉴于简单的设计和您想要的查询和更新类型,它可以重新排列为另一种设计。其他设计的基础 table 是基本设计 table 的 views/queries,反之亦然。
我只在专门修改设计以促进更简单的约束表达时给出约束。基本谓词和每个业务规则可能出现的业务情况决定了可能出现的数据库状态以及数据库约束。我们不需要知道查询的约束。
我来这里是为了提出一个问题,过去几天我在开发数据库设计时一直在努力解决这个问题。
简介:
该数据库旨在存储有关电机类型的信息(不是关于实际电机,而是更多关于它们的设计、参数等)、它们的 components/attributes 以及某些电机类型之间的关系(每个电机类型都与所谓的基本电机类型相关) .所以我们有几个电机类型,每个电机类型都有 many 属性(我称它们为 detailtypes)。每个细节类型依次可以出现在 一个或多个 电机类型中。每个 motortype:detailtype 组合应该(恰好)有一个值。
我需要能够比较两种不同电机类型的特定细节类型的值,并根据此比较存储信息。 (存储此信息是数据库的中心目的。)
现在第一个也是最简单的方法是制作一个 motortype table 并使其每个 detailtype 都有一列,这样我就可以为每个组合存储一个值:
Approach #1:
CREATE TABLE Motortype(
id INTEGER PRIMARY KEY,
description CHAR (50),
detailtype1 CHAR (50),
detailtype2 INTEGER,
detailtype3 YES/NO,
detailtype4 DOUBLE );
大约有一百个(这个数字将来可能会慢慢增长)这样的细节类型要输入到数据库中。添加更多对应于添加更多列(可管理),更改它们将导致重命名列(坏主意?)。当我想将电机类型与它们的 detailtype2 值上的 ID 2 和 5 进行比较并保存有关此比较的信息时,就会出现问题。我不知道怎么办。 (此外,属性不能具有 NOT NULL 约束,因为在创建条目时某些数据可能只是未知的...)
这就是我继续使用方法 2 的原因,它似乎以某种方式实现了 EAV。
为了允许存储比较信息,我将 detailtypes 从属性移动到 mototype 到它们自己的实体,同时将不需要比较的那些保留为属性:
Approach #2:
CREATE TABLE Motortype(
id INTEGER PRIMARY KEY,
description CHAR (50),
notComparableAttribute1 CHAR (50),
notComparableAttribute2 INTEGER
);
CREATE TABLE Detailtype(
id INTEGER PRIMARY KEY,
description CHAR (50),
datatype CHAR (3)
);
CREATE TABLE MotorDetail(
id INTEGER PRIMARY KEY,
motortype_id INTEGER CONSTRAINT fk_mt_id REFERENCES Motortype(id) NOT NULL,
detailtype_id INTEGER CONSTRAINT fk_dt_id REFERENCES Detailtype(id) NOT NULL,
value CHAR (50) NOT NULL
);
然后,此配置允许我引用 MotorDetail 中的任何特定值对并保存此比较的附加信息。 (请注意,此比较有更多维度 - 它与 MotorDetail 没有 1:1 关系。而是 1:n MotorDetail:ComparisonData 关系。否则我可以直接存储所有比较信息在 MotorDetail 中。)
然而,事实上我现在有一个需要保存多种不同数据类型(字符串、整数、浮点数、布尔值)的值属性,这意味着输入验证的责任从数据库转移到了我身上(我需要确保用户只能以对相应 Detailtype 有意义的方式输入数据)。这完全没问题 - 只是多了一点编程工作,但我觉得它可以用更优雅的方式解决。
所以在对多态关联进行一些研究之后,我想到了下一个方法(Motortype 和 Detailtype 与 #2 中的一样):
Approach #3:
CREATE TABLE MotorDetail(
id INTEGER PRIMARY KEY,
motortype_id INTEGER CONSTRAINT fk_mt_id REFERENCES Motortype(id) NOT NULL,
detailtype_id INTEGER CONSTRAINT fk_dt_id REFERENCES Detailtype(id) NOT NULL,
valueTxt CHAR (50),
valueInt INTEGER,
valueDbl DOUBLE,
valueBoo YES/NO
);
值属性不能有 NOT NULL 约束,因为 75% 的值将为空(根据要求,每个 MotorDetail 只能有一个值)。只要我只需要特定数据类型的值,查询值似乎就可以,否则它会变得更加复杂(例如,将它们显示在表单上)。或者我应该说 乏味 而不是复杂。
因此,在最后一次尝试解决这个问题时,我提出了我的最终方法(同样是#2 中的 Motortype 和 Detailtype):
Approach #4:
CREATE TABLE MotorDetail(
id INTEGER PRIMARY KEY,
motortype_id INTEGER CONSTRAINT fk_mt_id REFERENCES Motortype(id) NOT NULL,
detailtype_id INTEGER CONSTRAINT fk_dt_id REFERENCES Detailtype(id) NOT NULL
);
CREATE TABLE MotorDetailValueTxt(
id INTEGER PRIMARY KEY,
valueTxt CHAR (50)
);
CREATE TABLE MotorDetailValueInt(
id INTEGER PRIMARY KEY,
valueTxt INTEGER
);
... (same for MotorDetailValueDbl and MotorDetailValueBoo)
我不确定这个。不确定如何表示值 tables 中的每个 id 都与一个 MotorDetail(id) 绑定并且需要在 all 值 tables。甚至不确定我是否完全理解了基本概念。但我可以肯定的是,这样做数据库无法强制执行参照完整性。这就是为什么我不打算使用它。在测试数据库中,我能够使用 UNION 生成查询以获取所有不同的值,但我认为我不想在 table 条目级别进行任何内务处理(以某种方式确保没有死条目) .
问题:
鉴于目前概述的详细信息,是否有可能的解决方案不涉及某种编码魔法(VBA 或其他)?一个优雅的解决方案,以某种方式允许在数据库级别处理这一切(tables+关系,而不是报告、表格等...)?由于到目前为止我提出的所有内容似乎都很笨拙。
注意:我是数据库设计和一般数据库的新手。我已经接受了 3 天的 MS Access 培训,但这主要是为了学习 GUI 和构建一些非常基本的数据库而量身定制的。关于数据库开发,我知道(或假设知道)的任何其他事情都是通过阅读博客了解到的——最重要的是:关于 SO 的问题和答案。
可以肯定的是,我没有(完全)理解我所讨论的一个或多个概念,因此歪曲了其中的一些概念。如果是这样,或者如果有任何遗漏,请在评论中向我指出或进行编辑以改进我的问题(并允许我阅读它)。 :)
编辑:进行了一次编辑,而且是一个很大的编辑。我试图添加信息,澄清那里的内容并仍然保持问题的原始特征和意图。希望我至少部分成功了。 :)
你的问题看起来比你的设计比较简单和基础。对于您的应用程序的给定片段,这似乎比必要或期望的更复杂。
业务关系(船舶)/协会由 table 表示。我们可以用一个predicate——一个列参数化的语句模板来表征一个关系(ship)/association/table。 table 包含产生真实 命题 --陈述的行。
你没有给出比以下更复杂的设计的任何理由:
-- motor motorID has (non-details) ...
Motor(motorID, ...)
-- motor motorID has NumOfCylinders cylinders
MotorNumOfCylinders(motorId, NumOfCylinders)
-- motor motorID has cylinder material CylinderMaterial
MotorCylinderMaterial(motorId, CylinderMaterial)
...
你只想要一台电机吗table?
-- motor motorID has (non-details) ...
-- AND motor motorID has NumOfCylinders cylinders
-- AND ( NumOfCylinders > 0 and motor motorID has cylinder material CylinderMaterial ...
-- OR NumOfCylinders = 0 and CylinderMaterial = NULL ...
-- )
...
-- PK (motorID)
Motor(motorId, ..., NumOfCylinders, CylinderMaterial, ...)
您还没有解释随着时间的推移会发生什么变化。您想要更改字符串与给定详细信息的关联吗?对于可选字符串:
-- detail "NumOfCylinders" is written NumOfCylinders
NumOfCylindersString(NumOfCylinders)
-- detail "CylinderMaterial" is written CylinderMaterial
CylinderMaterialString(CylinderMaterial)
...
您只想要一个这样的 table 吗?对于必填字符串:
-- detail "NumOfCylinders" is written NumOfCylinders
-- AND detail "CylinderMaterial" is written CylinderMaterial
...
DetailStrings(NumOfCylinders, CylinderMaterial, ...)
您是否明确想要不同的 kinds/types 电机,以便在处理特定 kind/type 时具有静态约束?
-- motor motorID ... and has NumOfCylinders cylinders and weighs Weight kg and ...
Motor(motorId, ..., NumOfCylinders, Weight, ...)
-- piston motor motorID has cylinder material CylinderMaterial ...
PistonMotor(motorId, CylinderMaterial, ...)
-- electric motor motorId has ...
-- and it is isAC that it takes AC current
-- and it is isDC that it takes DC current
...
ElectricMotor(motorId, isAC, isDC, ...)
...
是否要以冗余和计算为代价将电机限制为恰好 kind/type?
-- motor motorId is of type motorType and ...
Motor(motorId, motorType, ...)
-- PistonMotor enforce (motorId, 'piston') in (select motorId, motorType from Motor)
-- ElectricMotor enforce (motorId, 'electric') in (select motorId, motorType from Motor)
...
是否要根据不同的成本权衡(通过存储或生成的列值)以声明方式进行约束?
-- motor motorID has type motorType and motorType = 'piston' and cylinder material CylinderMaterial ...
-- FK (motorID, motorType) references Motor (motorID, motorType)
-- check (motorType = 'piston')
PistonMotor(motorId, motorType, CylinderMaterial, ...)
-- motor motorID has type motorType and motorType = 'electric' and ...
-- FK (motorID, motorType) references Motor (motorID, motorType)
-- check (motorType = 'electric')
ElectricMotor(motorId, motorType, isAC, isDC, ...)
...
更多?
-- Motor check (NOT (motorType = 'electric' AND NumOfCylinders <> 0))
随着时间的推移,一个细节可以有不同的值吗?你想随着时间的推移有不同的细节吗?您可以使用 DDL 来实现更改。您想知道当前有哪些详细信息或详细信息值吗?查询元数据。或者可能有一些非系统 table 或其枢轴 1:1 与系统 table 或其枢轴的行。
您是否以某种方式 think/assume/suspect/fear 以牺牲复杂性和处理为代价,通过 DML 实现用户控制的状态更改比通过 DDL 更好? DDL 更新实施是否被证明太慢?您可以通过 EAV 及其伴随的优缺点对这些数据库状态进行部分或全部编码。
An EAV 'database' [sic] is literally mathematically straightforwardly an undocumented description in triples of a database and its metadata, with no functionality to
To paraphrase Greenspun, any sufficiently complex EAV project contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of a DBMS.
(请注意,在 EAV 中,一列给出了另一列的类型——其中数据对于表示是必不可少的——并且在一些子类型习语中——作为冗余的变体标签并用于约束。
鉴于简单的设计和您想要的查询和更新类型,它可以重新排列为另一种设计。其他设计的基础 table 是基本设计 table 的 views/queries,反之亦然。
我只在专门修改设计以促进更简单的约束表达时给出约束。基本谓词和每个业务规则可能出现的业务情况决定了可能出现的数据库状态以及数据库约束。我们不需要知道查询的约束。