表设计和类层次结构
希望有人可以通过一个例子或一些建议的阅读来阐明这个问题。 我想知道在类层次结构等效之后对表进行建模的最佳设计方法是什么。这可以通过一个例子来最好地描述:
abstract class Card{
private $_name = '';
private $_text = '';
}
class MtgCard extends Card{
private $_manaCost = '';
private $_power = 0;
private $_toughness = 0;
private $_loyalty = 0;
}
class PokemonCard extends Card{
private $_energyType = '';
private $_hp = 0;
private $_retreatCost = 0;
}
现在,当对表进行建模以与此类层次结构同步时,我采用了非常相似的方法:
TABLE Card
id INT, AUTO_INCREMENT, PK
name VARCHAR(255)
text TEXT
TABLE MtgCard
id INT, AUTO_INCREMENT, PK
card_id INT, FK(card.id)
manacost VARCHAR(32)
power INT
toughness INT
loyalty INT
TABLE PokemonCard
id INT, AUTO_INCREMENT, PK
card_id INT, FK(card.id)
hp INT
energytype ENUM(...)
retreatcost INT
我遇到的问题是试图找出如何关联每个 Card< /code> 记录包含相应表中的详细信息。具体来说,如何确定我应该查找哪个表。
我应该向 Card
添加一个 VARCHAR
列来保存关联表的名称吗?这是我和我的同事们达成的唯一决议,但它似乎太“肮脏”了。 保持设计的可扩展性是这里的关键,可以轻松添加新的子类。
如果有人可以提供一个示例或资源来展示镜像类/表层次结构的干净方法,我们将不胜感激。
Hopefully someone can shed some light on this issue through either an example, or perhaps some suggested reading. I'm wondering what is the best design approach for modeling tables after their class hierarchy equivalencies. This can best be described through an example:
abstract class Card{
private $_name = '';
private $_text = '';
}
class MtgCard extends Card{
private $_manaCost = '';
private $_power = 0;
private $_toughness = 0;
private $_loyalty = 0;
}
class PokemonCard extends Card{
private $_energyType = '';
private $_hp = 0;
private $_retreatCost = 0;
}
Now, when modeling tables to synchronize with this class hierarchy, I've gone with something very similar:
TABLE Card
id INT, AUTO_INCREMENT, PK
name VARCHAR(255)
text TEXT
TABLE MtgCard
id INT, AUTO_INCREMENT, PK
card_id INT, FK(card.id)
manacost VARCHAR(32)
power INT
toughness INT
loyalty INT
TABLE PokemonCard
id INT, AUTO_INCREMENT, PK
card_id INT, FK(card.id)
hp INT
energytype ENUM(...)
retreatcost INT
The problem I'm having is trying to figure out how to associate each Card
record with the record containing it's details from the corresponding table. Specifically, how to determine what table I should be looking in.
Should I add a VARCHAR
column to Card
to hold the name of the associated table? That's the only resolution that my peers and I have come to, but it seems too "dirty". Keeping the design extensible is the key here, allowing for the easy addition of new subclasses.
If someone could provide an example or resources showing a clean way of mirroring class/table hierarchies, it would be most appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
谷歌“泛化专业化关系建模”。您将找到几篇关于如何使用关系表对 gen-spec 模式进行建模的优秀文章。同样的问题在 SO 中被问过很多次,但细节略有不同。
这些文章中最好的一篇将确认您的决定,即使用一张表来存储通用数据,并使用单独的表来存储专门数据。最大的区别是他们建议使用主键和外键的方式。基本上,他们建议专用表有一个具有双重功能的列。它充当专用表的主键,但它也是复制通用表的 PK 的外键。
这维护起来有点复杂,但是在连接时却非常方便。
另请记住,将新类添加到层次结构时需要 DDL。
Google "generalization specialization relational modeling". You'll find several excellent articles on the subject of how to model the gen-spec pattern using relational tables. This same question has been asked many times in SO, with slightly different details.
The best of these articles will confirm your decision to have one table for generalized data and separate tables for specialized data. The biggest difference will be the way they recommend using primary and foreign keys. Basically, they recommend that specialized tables have a single column that does double duty. It serves as the primary key to the specialized table, but it's also a foreign key that duplicates the PK of the generalized table.
This is a little complicated to maintain, but it's very sweet at join time.
Also keep in mind that DDL is required when a new class is added to the hierarchy.
基本上不会。
忘记类层次结构、存储模型以及任何特定于您的应用程序和特定应用程序语言的内容。除非您想将 RDb 用作文件的单纯存储位置,否则它是一个从属奴隶。
如果您想要关系数据库的强大功能和灵活性(特别是可扩展性),那么您需要独立于任何应用程序对其进行建模,并使用 RDb 原则,而不是应用程序语言要求。暂时将您的应用程序上下文抛在一边,并将数据库设计为数据库。了解他们。标准化(消除所有重复)。了解结构和规则并实施它们。当您这样做时,您的查询和“映射”将毫不费力。不会有“阻抗”。使用正确的数据类型就不会出现不匹配的情况。
您需要的结构是普通的子类型-超类型。这些关系数据库术语在 RM 中已经存在了 30 多年,在关系数据库产品中已经存在了 23 多年。不需要给他们起有趣的新名字。维基百科不是学术参考。
鉴于您的表格作为起点非常正确(您已自动标准化),您需要:
将 Card.Id 重命名为 Card.CardId
删除子类型的 id,它们是 100% 冗余的; CardId既是PK又是FK。
添加鉴别器 Card.CardType CHAR(1) 或 TINYINT。当 CardType 未知时,这将识别要加入的子类型。
看来您还没有完全理解外键的概念,因此最好先做好准备。它在这里以简单、普通的形式实现:
Card 与 MtgCard 或 PokemonCard 之间的关系始终为 1::1。只有当有 Card 加上 { MtgCard | MtgCard | 时,超类型才是完整的。 PokemonCard } 具有相同的 CardId。在您的情况下,只能有一种子类型,可以通过简单的 CHECK 约束轻松执行。
在其他情况,不止一种子类型是完全合法的。
子类型有人是老师或人是学生
在关系数据库中,没有连接“从”或“到”(或上/下或左/右)的概念,这些概念只是为了帮助我们人类;您可以从您拥有的任何桌子/钥匙开始,然后转到您需要的任何桌子。仅在没有关系标识符的情况下才需要中间的表(即,使用其他代理项、ID 列作为 PK而不是有意义的自然键)。< /p>
。
快速参考标准关系数据库图。
Basically don't.
Forget about class hierarchies, storage models, and anything that is specific to your app and your particular app language. Unless you want to use the RDb as a mere storage location for your files, a dependent slave.
If you want the power and flexibility (specifically extensibility) of the relational Database, then you need to model it independent of any app, and using RDb principles, not app language requirements. Leave your app context behind for a while and design the database as a database. Learn about them. Normalise (eliminate all duplication). Learn about the structures and rules, and implement them. When you do that, your queries and your "mapping", will be effortless. There will be no "impedance". Use the correct datatypes and there will be no mismatch.
The structure you require is an ordinary subtype-supertype. Those are Relational Database terms that have been in existence for over 30 years in the RM, and over 23 years in Relational Database products. No need to call them funny new names. Wikipedia is not an academic reference.
Given your tables, which are quite correct as a starting point (you've Normalised automatically), you need:
Rename Card.Id as Card.CardId
Remove the ids for the subtypes, they are 100% redundant; the CardId is both the PK and the FK.
Add a discriminator Card.CardType CHAR(1) or TINYINT. This will identify which subtype to join with, when the CardType is not known.
It appears you do not fully understand the concept of Foreign Keys, so that would be good to gear up on first. It is implemented here in its simple, ordinary form:
The relation between Card and MtgCard or PokemonCard is always 1::1. The supertype is complete only when there is a Card plus { MtgCard | PokemonCard } with the same CardId. In your case there can be only one subtype, easy to enforce with a simple CHECK constraint.
In other cases, more than one subtype is quite legal.
The subtypes there are Person Is a Teacher or Person Is a Student
In Relational Databases there is no concept of joining "from" or "to" (or up/down or left/right), those notions are only there to assist us humans; you can start with any table/key you have, and go to any table you need. The tables in-between are demanded only in the absence of Relational Identifiers (ie. where additional Surrogates, ID columns, are used as PKs instead of meaningful natural keys).
.
Quick Reference to Standard Relational Database Diagrams.