设计 SQL 数据库来表示 OO 类层次结构
Posted
技术标签:
【中文标题】设计 SQL 数据库来表示 OO 类层次结构【英文标题】:Designing SQL database to represent OO class hierarchy 【发布时间】:2010-08-05 09:41:05 【问题描述】:我正在将类层次结构转换为存储在 SQL 数据库中。
原伪代码:
abstract class Note
int id;
string message;
;
class TimeNote : public Note
time_t time;
;
class TimeRangeNote : public Note
time_t begin;
time_t end;
;
class EventNote : public Note
int event_id;
;
// More classes deriving from Note excluded.
目前我有几个想法如何将其存储在数据库中。
A.将所有笔记存储在一个宽表中
该表将包含派生自Note
的所有类所需的所有信息。
CREATE TABLE t_note(
id INTEGER PRIMARY KEY,
message TEXT,
time DATETIME,
begin DATETIME,
end DATETIME,
event_id INTEGER
);
从Note
派生的未来类需要向此表添加新列。
B.将每个类映射到一个表
CREATE TABLE t_note(
id INTEGER PRIMARY KEY,
message TEXT
);
CREATE TABLE t_timenote(
note_id INTEGER PRIMARY KEY REFERENCES t_note(id),
time DATETIME
);
CREATE TABLE t_timerangenote(
note_id INTEGER PRIMARY KEY REFERENCES t_note(id),
begin DATETIME,
end DATETIME
);
CREATE TABLE t_eventnote(
note_id INTEGER PRIMARY KEY REFERENCES t_note(id),
event_id INTEGER
);
从Note
派生的未来类需要创建一个新表。
C.使用数据库规范化和VARIANT
/SQL_VARIANT
CREATE TABLE t_note(
id INTEGER PRIMARY KEY,
message TEXT
);
CREATE TABLE t_notedata(
note_id INTEGER REFERENCES t_note(id),
variable_id TEXT, -- or "variable_id INTEGER REFERENCES t_variable(id)".
-- where t_variable has information of each variable.
value VARIANT
);
从Note
派生的未来类需要添加新的variable_id
。
D.将每个具体类映射到一个表 (根据当前答案新添加)
CREATE TABLE t_timenote(
id INTEGER PRIMARY KEY,
message TEXT,
time DATETIME
);
CREATE TABLE t_timerangenote(
id INTEGER PRIMARY KEY,
message TEXT,
begin DATETIME,
end DATETIME
);
CREATE TABLE t_eventnote(
id INTEGER PRIMARY KEY,
message TEXT,
event_id INTEGER
);
从Note
派生的未来类需要创建一个新表。
SQL 中最合乎逻辑的表示是什么? 有没有更好的选择?
【问题讨论】:
在早期设计阶段,我喜欢将我的名词等同于类 和 表,而将动词等同于方法......这就是选项 B 所描述的。这似乎是做事最自然的方式。 【参考方案1】:一般来说,我更喜欢 obtion "B"(即一个表用于基类,一个表用于每个“具体”子类)。
当然,这有几个缺点:首先,当您必须读取子类的完整实例时,您必须连接至少 2 个表。此外,任何必须对任何类型的笔记进行操作的人都将不断访问“基础”表。
但这通常是可以接受的,除非您遇到极端情况(数十亿行、需要非常快的响应时间等等)。
还有第三种可能的选择:将每个子类映射到不同的表。这有助于对对象进行分区,但通常会花费更多的开发精力。
完整的讨论请参见this。
(关于您的“C”解决方案,使用 VARIANT:我无法评论优点/缺点,因为它看起来像一个专有解决方案 - 它是什么?Transact-SQL?我不熟悉它)。
【讨论】:
sql_variant
是 Microsoft SQL Server 的一种数据类型。我不想使用它。
我倾向于选项 B。将每个子类映射到不同的表听起来很有趣。一个缺点是它会阻止数据库中的其他表引用任何注释(即 REFERENCES t_note(id))。另一个缺点是,正如您所说,它的开发成本更高。
+1 最逻辑的事情是B。最合乎逻辑是因为从概念上讲它是最正确的方法;其余的作为物理设计是可以接受的,当您对数据库设计进行非规范化以在某些情况下获得更好的性能时(换句话说,您正在映射对象这一事实并没有真正使其成为一个特定问题,它属于一般数据库非规范化)。
B 是唯一的关系选项。我不认为 INNER JOIN 是复杂的 SQL,只是 SQL...【参考方案2】:
您所描述的“B”选项几乎是“对象子类层次结构”的实现(Kung,1990 http://portal.acm.org/citation.cfm?id=79213)
因此,这是一种成熟且易于理解的方法。它工作得很好。如果需要,它还可以通过多级继承进行扩展。
如果您不通过 DBMS 界面限制谁可以访问数据,那么您当然会失去封装和信息隐藏的一些好处。
但是,您可以同时从多个系统甚至语言(例如 Java、C++、C#)访问它 (这是我硕士论文的主题:)
【讨论】:
【参考方案3】:您已经了解了将对象建模到关系数据库中的 3 种最普遍接受的方法。这三个都可以接受,每个都有自己的优点和缺点。不幸的是,这意味着没有简单的“正确”答案。我已经在不同的时间实现了其中的每一个,这里有一些注意事项/注意事项要牢记:
选项 A 的缺点是,当您添加新的子类时,您必须修改现有的表(这对您来说可能比添加新表更不受欢迎)。它还有一个缺点,即许多列将包含 NULL。然而,现代数据库在管理空间方面似乎比旧数据库好得多,所以我从不担心空值。一个好处是您的任何搜索或检索操作都不需要 JOIN 或 UNION,这意味着可能会获得更好的性能和更简单的 SQL。
选项 B 的缺点是,如果向超类添加新属性,则需要向每个子类的表中添加新列。此外,如果您想进行异构搜索(一次所有子类),则必须使用 UNION 或 JOIN(可能会降低性能和/或更复杂的 sql)。
选项 C 的缺点是所有检索操作(即使只是一个子类)都将涉及 JOIN,大多数搜索也是如此。此外,所有插入都将涉及多个表,使得 SQL 更加复杂,并且需要使用事务。从数据规范化的角度来看,这个选项似乎是最“纯粹”的,但我很少使用它,因为每次操作都加入 JOIN 的缺点通常会使其他选项之一更受欢迎。
【讨论】:
抱歉,刚刚注意到这一点 - “如果您向超类添加新属性,则需要向每个子类的表添加新列”是什么意思?在 B 中,您可以将其添加到更抽象的类中,而无需添加到更具体的表中。如果您向 t_note 添加“评级”字段,则无需将其添加到其他子表中,即可返回原始状态。我错过了什么吗?【参考方案4】:我自己更喜欢选项 A。
这也取决于您的使用场景,例如您是否需要对所有类型的笔记进行大量搜索?如果是,那么您可能会选择 A 更好。
您始终可以将它们存储为选项 A(一个大表),并根据需要为不同的子注释创建视图。这样,您仍然可以在具有良好可搜索性的同时进行逻辑分离。
一般来说,但这可能接近于宗教讨论,所以要小心,我认为关系数据库应该是关系数据库,而不是试图模仿 OO 结构。让你的类做面向对象的东西,让数据库是相关的。如果您想将其扩展到您的数据存储,可以使用特定的 OO 数据库。这确实意味着您必须克服他们所说的“对象关系阻抗不匹配”,但同样有用于该特定目的的 ORM 映射器。
【讨论】:
【参考方案5】:我会选择选项 A。
如果类层次结构非常复杂,有几十个类相互继承,则解决方案 B 很好。这是最具可扩展性的解决方案。但是,缺点是它使 SQL 更复杂和更慢。
对于比较简单的情况,比如 4 或 5 个类都继承同一个基类,选择方案 A 更有意义。SQL 会更简单更快。并且具有 NULL 值的附加列的开销可以忽略不计。
【讨论】:
【参考方案6】:我已经使用了多年的一系列模式,统称为“Crossing Chasms”。不要让对 Smalltalk 的引用让你失望——它适用于任何面向对象的语言。尝试以下参考:
A Pattern Language for Relational Databases and SmalltalkCrossing Chasms - The Static PatternsCrossing Chasms - The Architectural Patterns
分享和享受。
编辑
Wayback Machine 链接到我在 Crossing Chasms 模式中找到的所有内容: http://web.archive.org/web/20040604122702/http://www.ksccary.com/article1.htm http://web.archive.org/web/20040604123327/http://www.ksccary.com/article2.htm http://web.archive.org/web/20040604010736/http://www.ksccary.com/article5.htm http://web.archive.org/web/20030402004741/http://members.aol.com/kgb1001001/Chasms.htm http://web.archive.org/web/20060922233842/http://people.engr.ncsu.edu/efg/591O/s98/lectures/persistent-patterns/chasms.pdf http://web.archive.org/web/20081119235258/http://www.smalltalktraining.com/articles/crossingchasms.htm http://web.archive.org/web/20081120000232/http://www.smalltalktraining.com/articles/staticpatterns.htm
我创建了一个 Word 文档,它将上述所有内容整合成一个类似连贯整体的东西,但我没有服务器可以将它放到公开可用的地方。如果有人可以建议一个免费的文档存储库,我很乐意将文档放在那里。
【讨论】:
以上链接失效【参考方案7】:我知道这个问题很老,但我有另一个选择:
您可以将 Note 对象或 Note 对象集合作为 json 结构存储在任何表列(文本类型)中。您可以使用Newtonsoft 序列化和反序列化 json。你需要specifies type name handling options to Object for the JsonSerializer。
【讨论】:
以上是关于设计 SQL 数据库来表示 OO 类层次结构的主要内容,如果未能解决你的问题,请参考以下文章