规范化这个数据库:在这种情况下啥是理想的?

Posted

技术标签:

【中文标题】规范化这个数据库:在这种情况下啥是理想的?【英文标题】:Normalizing this database: what would be ideal in this scenario?规范化这个数据库:在这种情况下什么是理想的? 【发布时间】:2011-03-19 08:32:19 【问题描述】:

我正在设计一个游戏,其中一个角色有很多物品,而这些物品可以有多种类型。有一个字符表,以及按类型(例如武器、盔甲和各种其他项目类型)细分的十二个不同的可能项目表。

我想创建一个表来保存这些项目类型的实例(基本上是一个角色的项目表),每一行都会有一个来自角色表的外键来指示哪个角色拥有该项目的所有权。

起初我想我会在角色的项目表中创建外键——十二个项目表中的每一个都有一个键。但由于每个项目只能是一种“类型”,这将导致每行中有 11 个空字段,这似乎是错误的。

什么是更好的方法?我还没有建立数据库,所以我可以考虑不使用十二个项目表的其他库存想法,但知道这一点:管理界面将允许用户根据需要添加/删除/修改每种类型的项目。

另外,我想坚持最好的规范化做法,所以我会忽略不可避免的“谁在乎?只做有效的事情并使用可为空的字段。”

【问题讨论】:

最佳规范化实践并不总是最佳编程实践。特别是,当您可以保证只从其中 1 个表中返回行时,任何需要您查询 12 个表的操作都不是最佳编程实践,无论您的数据集多么紧凑和非冗余。您对该系统的优先事项是什么? @Kylotan +1 - 应该始终应用归一化,但这是一种平衡行为,理论上你可以归一化到第 N 度,但此时你将有很多表,速度变慢您的查询性能。 【参考方案1】:

我会先检查您是否可以将这十二张表统一为一张。从Single-Table Inheritance 开始,因为它很简单,我敢打赌在这种情况下就足够了。

CHARACTER --<- CHAR_ITEMS  ->-- ITEM_TYPES_STI 

如果没有,请尝试Class Table Inheritance。基本上,为“项目类型”定义一个超表,角色的项目引用它。 12 个单独项目类型的每个子表也引用了项目类型超表:

CHARACTER --<- CHAR_ITEMS  ->-- ITEM_TYPES_SUPER --- ITEM_TYPE_SWORDS

关于您的评论:我已经澄清了上面的那一行。 item_types_super 和 item_type_swords 的关系应该是 1:1。也就是说,对于剑中的每一行,在 super 中应该有一个不同的行。这样做的方法是让刀剑中的外键成为它的主键。

但不是 super 中的每一行都有一排剑。 super 中的行可以被 shield、axes 或其他任何东西中的行引用。但只有一种亚型。这个想法是所有项目类型共有的列都属于 super,而特定于子类型的列在每个子类型表中。

【讨论】:

如果我理解你的第二个例子:一个角色有很多 char_items,一个 item_type_super 有很多 char_items 和 item_type_swords。如果管理员在 item_type_swords 表中插入了几把剑,并且 char_items 表中的一行有一个映射到 item_types_super 表中的“剑”类型的键,我如何从 item_type_swords 表中判断该项目打算用于哪把剑是吗? 谢谢,我去看看会发生什么!【参考方案2】:

在您的情况下,我会尝试将所有十二个表合并回一个表。

这样做的原因实际上与技术表设计无关——而是它会让你的角色以意想不到的方式重复使用对象。

例如,您可以拥有一组可投掷属性,例如 EFFORT、ACCURACY、DAMAGE、BREAKAGE,因此匕首几乎可以毫不费力地投掷,并且如果它卡住目标会造成很大的伤害,另一方面高脚杯很容易扔,但造成的伤害很小,或者金条很难扔,但即使严格来说不是武器,也会造成合理的伤害。 它还可以让你拥有武器,这些武器也是具有巨大价值或魔法用途的“宝藏”物品(不知道你正在计划什么样的游戏,所以这些例子可能不合适

【讨论】:

【参考方案3】:

如果您使用了下表(我只包括了列的键值)。 CHARACTERS_ITEMS 表包含每个字符的项目列表。 ItemTypeID 和 ItemID 的组合将用于引用 12 个项目表之一中的一行。

CHARACTERS
----------
CharacterID (PK)

ITEMS
-----
ItemID (PK)

ITEM_TYPES
----------
ItemTypeID (PK)

CHARACTERS_ITEMS
----------------
CharacterID (FK)
ItemTypeID (FK)
ItemID (FK)

这里是一个可能有帮助的视图的开始。它演示了如何使用 ItemTypeId 来引用相应的表。

CREATE VIEW v_character_items
AS
SELECT list of columns
FROM character_items
JOIN character_item_1 on character_items.ItemID = character_item_1.ItemID and character_items.ItemTypeID = 1

UNION 
SELECT list of columns
FROM character_items
JOIN character_item_2 ON character_items.ItemID = character_item_2.ItemID and character_items.ItemTypeID = 2

UNION
other tables...

【讨论】:

我正在审查你的设计,看看它是否适合我,但我不得不说:为什么要使用UNION?我相信你应该考虑学习JOIN 语法。 谢谢@Stephen。我相信我有很多东西要向你学习。我不知道您可以在 SQL 中执行 JOIN。假设您要显示以下列 character_items.CharacterID、character_items.ItemId 以及表 character_item_1、character_item_2 等中的列。如果我用 JOINS 替换 UNION,您能告诉我 SELECT 子句会是什么样子吗? SELECT character_items.CharacterID, character_items.ItemId, character_item_1.*, character_item_2.* FROM character_items JOIN character_item_1 ON character_items.ItemID = character_item_1.ItemID JOIN character_item_2 ON character_items.ItemID = character_item_2.ItemID; @Stephen - 您的 SELECT 语句假定每个表中每个字符只有一行。如果一个角色没有所有项目类型之一,则该角色将不会包含在结果中。如果一个字符在任何项目表中具有多行,则您的查询将为每个字符生成多行并重复数据。嗯,也许我对 JOIN 语法有点了解。 mysql 中,默认情况下连接是 LEFT。为了清楚起见,我应该指定LEFT JOIN(以防您不使用MySQL)。因此,在LEFT JOIN 中,character_items.CharacterID 和 character_items.ItemId 将始终返回。其他表中缺少的表数据将为 NULL。并且来自 character_items 表的重复数据比多选更可取。尤其是12选。我希望这会有所帮助。 BTW:我不是在攻击你,我是想帮你。【参考方案4】:

我不知道你是否还在寻找回复,但这是我最近的做法:

CHARACTER -> Char/Item Link Table -> ITEM

ITEM
----
ID
NAME
TYPE
etc.

这允许您创建多个角色可以拥有的基础项目。如果您想对可以通过效果表(或类似的东西)轻松完成的项目(即宝石槽、附魔等)进行个性化修改:

ITEM -> Item/Effect Link Table -> EFFECTS

通过这种方式,您可以让角色自定义他们自己的武器,同时仍然保留基地。您还可以创建一组效果,而无需为游戏中的每个项目一遍又一遍地重复它们。

【讨论】:

以上是关于规范化这个数据库:在这种情况下啥是理想的?的主要内容,如果未能解决你的问题,请参考以下文章

请解释下啥是三视图中的实线与虚线重合

为啥 QWebFrame::evalutateJavaScript 在这种情况下啥都不做?

正确测试方法在某些情况下啥都不做

[SQL] 请教一下 count里面有case when 一般情况下啥时候用

wmic.exe的一、啥是WMIC?

PHP Jquery:聊天系统,啥是理想的框架?