【问题标题】:Do I need a polymorphic association here or is my model not fleshed out enough?我在这里需要多态关联还是我的模型不够充实?
【发布时间】:2018-01-30 23:13:42
【问题描述】:

我来这里是为了提出一个问题,我在过去几天开发数据库设计时一直在努力解决这个问题。

简介:
该数据库旨在存储有关电机类型的信息(不是关于实际电机,而是更多关于它们的设计、参数等)、它们的组件/属性以及某些电机类型之间的关系(每个电机类型都与所谓的基本电机类型相关)。所以我们有几个motortypes,每个都有许多属性(我称之为detailtypes)。每个细节类型依次可以出现在一个或多个电机类型中。每个 motortype:detailtype 组合应该(完全)有一个值。
我需要能够比较两种不同电机类型的特定详细信息类型的值,并根据此比较存储信息。 (此信息的存储是数据库的中心目的。)


现在第一个也是最简单的方法是制作一个 motortype 表并使其每个 detailtype 都有一个列,这样我就可以为每个组合存储一个值:

Approach #1:

CREATE TABLE Motortype(
id INTEGER PRIMARY KEY,
description CHAR (50),
detailtype1 CHAR (50),
detailtype2 INTEGER,
detailtype3 YES/NO,
detailtype4 DOUBLE );

将有大约一百个(这个数字在未来可能会缓慢增长)这样的详细信息类型要输入到数据库中。添加更多对应于添加更多列(可管理),更改它们会导致重命名列(坏主意?)。当我想将电机类型与 ID 2 和 5 的 detailtype2 值进行比较并保存有关此比较的信息时,就会出现问题。我不知道如何管理它。 (此外,属性不能有 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 中。)
但是,我现在有一个 value 属性需要保存几种不同的数据类型(字符串、整数、浮点数、布尔值),这意味着输入验证的责任从数据库转移到了我身上(我需要确保用户只能以对相应 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 只能有一个值)。只要我只需要特定数据类型的值,查询值似乎就可以了,否则它会变得更加复杂(例如,在表单上显示它们)。或者我应该说乏味而不是复杂。


所以在最后一次尝试解决这个问题时,我提出了我的最终方法(再次,Motortype 和 Detailtype,如 #2 所示):

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)

我不确定这个。不确定如何表示值表中的每个 id 都与一个 MotorDetail(id) 相关联,并且需要在 所有 值表中是唯一的。甚至不确定我是否完全理解了基本概念。但我可以肯定的是,这样做数据库不能强制引用完整性。这就是我不打算使用它的原因。在一个测试数据库中,我能够使用 UNION 生成一个查询以获取所有不同的值,但我认为我不想在表条目级别进行任何内务管理(以某种方式确保没有死条目)。


问题:

鉴于到目前为止概述的详细信息,是否存在不涉及某种编码魔法(VBA 或其他)的可能解决方案?一个优雅的解决方案,以某种方式允许在数据库级别处理这一切(表+关系,而不是报告、表单等......)?到目前为止,我提出的所有内容都显得相当笨拙。


注意:我是数据库设计和一般数据库的新手。我在 MS Access 中接受了 3 天的培训,但这主要是针对学习 GUI 和构建一些非常基本的数据库而量身定制的。关于我知道(或假设知道)有关数据库开发的任何其他内容,都是通过阅读博客了解到的 - 最重要的是:SO 上的问题和答案。
可以假设我没有(完全)理解我讨论的一个或多个概念,因此歪曲了其中的一些概念,这似乎是安全的。如果是这样,或者如果缺少其他任何内容,请在 cmets 中向我指出或进行编辑以改进我的问题(并允许我阅读它)。 :)

编辑:进行了编辑,并且进行了很大的编辑。我试图添加信息,澄清那里的内容,并仍然保留问题的原始特征和意图。让我们希望我至少部分成功了。 :)

【问题讨论】:

  • @philipxy 感谢您的评论。阅读有关子类型/多态性是导致这个问题的原因。我目前正在阅读“FKs to multiple/two/many tables”。我当然不打算使用 EAV,也许我的带有示例值的表是以令人困惑的方式编写的?我将尝试在编辑中澄清这一点。我确实有几本关于数据库设计的书,但是它们没有提到更复杂的关系(它们可能过于面向初学者)。实际上,我确实从一个非常简单的设计开始,但由于业务需求,它很快变得相当复杂。
  • @philipxy 关于“有问题的文本,不在链接/图像中”:这不是我的做法吗?唯一符合“在链接/图像中”的文本是图像“标题”中的文本(由于我还不允许发布内联图像)以及屏幕截图中的文本。我不确定你在这里的意思。并重新“使用代码格式格式化表格”:我做到了。我的问题中唯一的表格被格式化为代码。还是说我应该将链接图像中的关系呈现为 ASCII 艺术?
  • 我想我现在明白你对 EAV 的看法了。 :( 但我不确定以不同的方式进行操作实际上是否可行。如前所述,每个电机有大约 100 个详细信息。这将导致创建一个巨大的表(100 列)或大约 100 个带有巨大链接表的表在电机表和细节表之间。但无论如何我认为我需要回去重新评估我的模型。
  • 您的图片是 DDL 的图片。 Give the DDL. 无论如何你都应该这样做--minimal reproducible example。我不建议 EAV;只是一列在 EAV 中给出了另一列的类型——其中数据对于表示是必不可少的——在某些子类型习语中——作为冗余的变体标签并用于约束。重新设计一个初始设计,忘记“巨大”,找到简单的关系(船)/关联。稍后重新排列。记住——DBMS 已经有元数据和 DDL 命令。
  • 我怀疑您的问题比您的问题更简单和基本。同样,它的设计是一些尚未给出的简单设计的复杂版本。 (当然你可以给出一个与实际平行的简单例子?)然后是我们的问题,考虑到(组合)问题什么是合理的权衡和习语。关于我的最后一条评论,阅读使用“(数据库或 sql)子类型”、“许多空列或许多表”、“多个表的外键”和“EAV”的谷歌搜索“stackoverflow”的第一页命中。 (重新 EAV stackoverflow.com/a/23950836/3404097 和 stackoverflow.com/a/33557166/3404097)。祝你好运。

标签: ms-access database-design relational-database polymorphic-associations


【解决方案1】:

您的问题似乎比您的设计比较简单和基本。对于您的应用程序的给定片段,这似乎比必要或期望的要复杂。

业务关系(船舶)/关联由表格表示。我们可以通过一个谓词——一个列参数化的语句模板来描述一个关系(船)/关联/表。一张表包含产生真正命题--语句的行。

你没有给出任何比设计更复杂的理由:

-- motor motorID has (non-details) ...
Motor(motorID, ...)
-- motor motorID has NumOfCylinders cylinders
MotorNumOfCylinders(motorId, NumOfCylinders)
-- motor motorID has cylinder material CylinderMaterial
MotorCylinderMaterial(motorId, CylinderMaterial)
...

您只想要一个电机工作台吗?

--      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)
...

您只想要一张这样的桌子吗?对于强制性字符串:

--     detail "NumOfCylinders" is written NumOfCylinders
-- AND detail "CylinderMaterial" is written CylinderMaterial
...
DetailStrings(NumOfCylinders, CylinderMaterial, ...)

您是否明确需要不同种类/类型的电机,以便在处理特定种类/类型时具有静态约束?

-- 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, ...)
...

您是否想以冗余和计算为代价将电机限制为一种类型/类型?

-- 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 来实现更改。您想知道当前有哪些详细信息或详细值吗?查询元数据。或者可能有一些非系统表或其枢轴与系统表或其枢轴的行是 1:1。

您是否以某种方式认为/假设/怀疑/害怕通过 DML 实现用户控制的状态更改比通过 DDL 更好,但会牺牲复杂性和处理能力? DDL-update 实施是否被证明太慢了?您可以通过 EAV 对这些数据库状态进行部分或全部编码,并具有相应的优缺点。

EAV“数据库”[原文如此]字面意思数学上直截了当数据库及其元数据的三元组中的未记录描述,没有

的功能

套用 Greenspun 的话说,任何足够复杂的 EAV 项目都包含一个临时的、非正式指定的、漏洞百出的、缓慢的半个 DBMS 实施。

(请注意,在 EAV 中,一列给出了另一列的类型——其中数据对于表示是必不可少的——在某些子类型习语中——作为冗余的变体标签并用于约束。Is there a way to represent relationship between a tuple and to a table in E-R diagram? )

考虑到简单的设计以及您想要的查询和更新类型,可以将其重新排列为另一种设计。其他设计的基表是基本设计表的视图/查询,反之亦然。

我仅在专门修改设计以促进更简单的约束表达时才给出约束。基本谓词和每个业务规则可能出现的业务情况决定了可能出现的数据库状态,从而决定了数据库约束。我们不需要知道查询的约束。

【讨论】:

  • 感谢您投入这么多时间来解决我的问题。我一直在阅读我的一本关于数据建模的书,它提供了一些关于如何从“用户场景”可靠地起草模型的非常有用的见解。 (其他书并没有花太多时间。)我怀疑在最后一次尝试中我可能跳过了一些重要的步骤。我现在将进行另一次设计,并会尽快报告!如果仍然有必要,我会为您的查询提供详细信息,或者修改我的问题 - 视情况而定。
  • 很抱歉没有回复 1:n 的事情。我的意思是避免出现方法 #2 中的情况,其中 MotorDetail_ID 是三个不同表的 FK,并且需要在所有三个表中都是唯一的(所以我基​​本上需要三个表的公共索引,Access 不提供)。我仍然不习惯用这种特定于数据库的语言说话,所以我很抱歉没有像我想要的那样清楚。如何继续我的问题?我是否应该通过回应您的澄清请求来使其更清楚一些?我应该将您的答案标记为已接受吗?
  • 谢谢。 (仍然感到困惑,例如 ERD 2 说 3 个 FK to MotorDetail。)重新编辑和/或重新询问,我建议澄清设计描述,但除此之外,寻求其他答案,或作为实践。您提到了约束问题,但没有给出约束。或者在某些情况下解释如何设置或解释表格,其中某些电机具有某些类型的某些详细信息或阐明约束条件!根据我的回答,约束不会那样做;谓词做。然后谓词和可能的情况确定约束。重新接受:meta.stackexchange.com/a/5235/266284
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 2021-11-02
  • 2013-05-04
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
相关资源
最近更新 更多