我正在将类层次结构转换为存储在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.
目前我有几个想法如何将其存储在数据库中。
:一种。将所有笔记存储在一个宽表中
该表将包含从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
派生的未来类需要创建一个新表。
℃。使用数据库规范化和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中最合乎逻辑的表示是什么?
还有更好的选择吗?
答案 0 :(得分:8)
一般来说,我更喜欢使用“B”(即一个基表和一个表,每个“具体”子类)。
当然这有一些缺点:首先,每当你必须阅读子类的完整实例时,你必须加入至少2个表。此外,任何必须操作任何类型笔记的人都会不断访问“基础”表。
但这通常是可以接受的,除非你有极端的情况(数十亿行,需要非常快的响应时间等等)。
还有第三种可能的选择:将每个子类映射到不同的表。这有助于对对象进行分区,但通常会在开发工作中花费更多。
有关完整讨论,请参阅this。
(关于你的“C”解决方案,使用VARIANT:我无法评论优点/缺点,因为它看起来像一个专有的解决方案 - 它是什么?Transact-SQL?我并不熟悉它)。
答案 1 :(得分:3)
所描述的'B'选项几乎是'Object Subclass Heirarchy'的实现(Kung,1990 http://portal.acm.org/citation.cfm?id=79213)
因此,它是一种成熟且理解的方法。它运作得很好。如果需要,它还可以通过多级继承进行扩展。
当然,如果您不限制谁可以通过DBMS接口访问数据,那么您将失去封装和信息隐藏的一些好处。
然而,您可以同时从多个系统甚至语言访问它(例如Java,C ++,C#) (这是我的硕士论文的主题:)
答案 2 :(得分:2)
您已经达到了将对象建模到关系数据库中的三种最常用的方式。所有3个都是可以接受的,每个都有自己的优点和缺点。不幸的是,这意味着没有任何干燥的“正确”答案。我已经在不同的时间实现了每一个,这里有几个注意事项/注意事项:
选项A的缺点是,当您添加新的子类时,您必须修改现有的表(这可能不像添加新表那样适合您)。它还有一个缺点,即许多列将包含NULL。但是,现代数据库在管理空间方面似乎比旧数据库更好,所以我从来不会太担心空值。一个好处是,您的搜索或检索操作都不会需要JOIN或UNION,这意味着可能会有更好的性能和更简单的SQL。
选项B的缺点是,如果向超类添加新属性,则需要向每个子类的表添加一个新列。此外,如果要进行异构搜索(所有子类一次),则必须使用UNION或JOIN(可能性能较低和/或更复杂的sql)。
选项C的缺点是,所有检索操作(即使只有一个子类)都将涉及JOIN,大多数搜索也是如此。此外,所有插入都将涉及多个表,这使得SQL更加复杂,并且需要使用事务。从数据规范化的角度来看,这个选项似乎是最“纯粹”的,但我很少使用它,因为JOIN-for-every-operation缺陷通常会使其他选项之一变得更加可口。
答案 3 :(得分:1)
我自己也会选择A。
它还取决于您的使用场景,例如,您是否需要在所有类型的笔记中进行大量搜索?如果是,那么选项A可能会更好。
您可以随时将它们存储为选项A(一个大表),如果您愿意,可以为不同的子注释创建视图。这样,在具有良好的可搜索性的同时,您仍然可以进行合理的分离。
一般来说,但这可能接近宗教讨论,所以要小心,我相信关系数据库应该是关系型数据库而不是试图模仿OO结构。让你的类做OO的东西,让db成为关系。如果要将此扩展到数据存储,则可以使用特定的OO数据库。它确实意味着你必须跨越'对象 - 关系阻抗不匹配',因为他们称之为,但同样有针对该特定目的的ORM映射器。
答案 4 :(得分:0)
我已经使用了很多年的一系列模式,统称为“Crossing Chasms”。不要让对Smalltalk的引用抛弃你 - 它适用于任何面向对象的语言。请尝试以下参考:
A Pattern Language for Relational Databases and Smalltalk
Crossing Chasms - The Static Patterns
Crossing 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文档,它将上述所有内容整合到一个类似于连贯整体的内容中,但是我没有服务器,我可以将其删除以使其公开。如果有人可以建议一个免费的文档存储库,我很乐意将文档放在那里。
答案 5 :(得分:0)
我会选择选项A 。
如果类层次结构非常复杂,并且有许多类继承彼此,那么解决方案B是很好的。这是最具扩展性的解决方案。但是,缺点是它使SQL变得更复杂和更慢。
对于相对简单的情况,如4或5个类都继承了相同的基类,选择解决方案A更有意义.SQL会更简单,更快。使用NULL值的其他列的开销可以忽略不计。
答案 6 :(得分:0)
我知道这个问题很旧,但是我还有另一个选择:
您可以在任何表列(文本类型)中将Note对象或Note对象集合存储为json结构。您可以使用Newtonsoft序列化和反序列化json。您需要specifies type name handling options to Object for the JsonSerializer。