如何为关系数据库中的可用库存/物品/物品(例如武士刀)建模多个“用途”(例如武器)


10

因此,我正在通过www.ninjawars.net扩展项目的用途,但我不确定如何在我们使用的关系数据库中灵活地表示它们。

我可能在树错了树上,所以可以随意向其他方向提出建议,但是目前我认为每个项目都应该有关系“标签”。

例如,Katana当前是“项”数据库中的一行。为了使它成为一种武器和一种可携带的东西,我想我要拥有一个“特征”数据库,以及一个将它们之间简单链接的item_traits表。

// Objects and their basic data

item_id | item
1 | Naginata


// Things that objects can do

trait_id | trait
1 | weapon
2 | holdable

// How those objects do those things, e.g. powerfully, weakly, while on fire

_item_id | _trait_id | item_trait_data
1 | 1 | damage: 5, damage_type: sharp, whatever, etc

我不太确定如何对产生的额外数据建模(例如,剑将造成的伤害,damage_type等)。

我也不太满意整个项目将存储在多个地方,例如,为了创建具有不同名称的项目的副本,例如“短剑”,我必须从多个表来创建重复项。

有没有更好的方法来布置我所缺少的东西?

编辑:我应该注意,我已经在该站点上使用了一个postgresql数据库,这就是为什么我想将其用于数据存储。

编辑:我已经为我当前正在寻找的实现添加了答案。

Answers:


10

我要稍微不同意所有人,并说关系方法在这里是合理的。这里有趣的是,项目可以具有多个角色。主要问题将是这种关系布局和代码中的OO布局之间的映射不会让人感到“自然”,但是我认为在数据库端可以清晰地表达多个角色(无需怪异的编码或冗余,只需加入即可) 。

首先要确定的是多少数据是特定于项目的,以及给定类型的所有项目共享多少数据。

如果所有数据都是特定于项目的,这就是我要做的事情:

// ITEMS table: attributes common to all items
item_id | name        | owner         | location             | sprite_id | ...
1       | Light Saber | 14 (Tchalvek) | 381 (Tchalvek house) | 5663      | ...

// WEAPONS table: attributes for items that are weapons
item_id | damage | damage_type | durability | ...
1       | 5      | sharp       | 13         | ...

// LIGHTING table: attributes for items that serve as lights
item_id | radius   | brightness | duration | ...
1       | 3 meters | 50         | 8 hours  | ...

在这种设计中,每个项目以及所有(或大多数)项目具有的属性都在“项目”表中。一个项目可以扮演的每个其他角色是一个单独的表。

如果您想将其用作武器,则可以在“武器”表中查找它。如果在那里,那么它可以用作武器。如果不存在,则不能用作武器。记录的存在告诉您它是否是武器。并且如果存在,则其所有特定于武器的属性都存储在此处。由于这些属性是直接存储的,而不是以某种编码形式存储的,因此您可以使用它们执行查询/过滤。(例如,对于游戏的指标页面,您可能希望按武器损坏类型来汇总玩家,并且可以通过一些联接和group-by Damage_type来做到这一点。)

一个项目可以具有多个角色,并且存在于多个角色特定的表中(在此示例中,武器和照明都存在)。

如果它只是一个布尔值,例如“这是可保持的”,我将其放入Items表中。可能值得在其中缓存“这是武器”等内容,这样就不必在武器和其他角色表上进行查找。但是,它增加了冗余,因此您必须小心使其保持同步。

如果某些数据不会因每个项目而有所不同,则Ari建议在每种类型中都有一个附加表的建议也可以使用此方法。例如,如果武器损坏在每个项目上没有变化,但是角色仍然在每个项目上变化,则可以将共享武器属性分解到表中:

// WEAPONS table: attributes for items that are weapons
item_id | durability | weapon_type
1       | 13         | light_saber

// WEAPONTYPES table: attributes for classes of weapons
weapon_type_id | damage | damage_type
light_saber    | 5      | energy

另一种方法是,项目所扮演的角色不会随项目而变化,而只会因项目类型而变化。在这种情况下,您可以将item_type放入Items表中,并可以在ItemTypes表中存储“是否是武器”,“是否可以固定”和“是否轻便”之类的属性。在此示例中,我还使项目名称不随每个项目而变化:

// ITEMS table: attributes per item
item_id | item_type    | owner         | location
1       | light_saber  | 14 (Tchalvek) | 381 (Tchalvek house)

// ITEMTYPES table: attributes shared by all items of a type
item_type   | name        | sprite_id | is_holdable | is_weapon | is_light
light_saber | Light Saber | 5663      | true        | true      | true

// WEAPONTYPES table: attributes for item types that are also weapons
item_type   | damage | damage_type
light_saber | 5      | energy

物品类型和武器类型很可能在游戏中不会改变,因此您可以将这些表一次加载到内存中,然后在哈希表中查找这些属性,而不用数据库联接。


+1。这是将关系数据库用作关系数据库的唯一答案。除了这个或Nevermind的blob建议的另一个极端,仅将DB用作数据存储,这都是可怕的计划。

+1但是,这些方法似乎有些不灵活。如果听起来不一致,我深表歉意,但是我希望能够为项目创建一次数据库结构……...然后为项目编写更多功能,为项目插入更多数据库,但不必更改将来再次将数据库(当然,很少有例外)。
卡扎伊州2010年

如果希望关系数据库了解您的字段并优化它们的表示,则需要将它们添加到某个位置。就像静态类型一样。如果您希望编译器了解您的字段并优化它们的表示形式,则需要声明类型。另一种选择是动态类型化的方法,它被某些文档数据库使用,或者通过以某种方式将数据编码到关系数据库中来使用。数据库不会知道字段是什么或无法优化它们的存储,但是您将获得一些灵活性。
阿米特

4

不幸的是,在这种情况下,关系数据库(例如SQL)不足,非关系数据库(例如MongoDB)表现出色。话虽如此,对关系数据库中的数据进行建模并不是不可能,并且由于您的代码库似乎已经依赖于SQL,因此我将使用以下模型:

代替创建项目和使用表,而是为每种项目类型创建一个表和“ [item] _type”引用表。

这里有一些例子:

weapon_type_id | weapon_type
1 | sharp

weapon_id | weapon| weapon_type_id | damage
1 | Short Sword | 1 | 5

potion_type_id | potion_type
1 | HP
2 | Mana

potion_id | potion| potion_type_id | modifier
1 | Healing Elixer | 1| 5
2 | Mana Drainer | 2| -5

该解决方案为您提供了许多长期的灵活性和可伸缩性,它减少了(如果不能消除)浪费的空间,并且具有自记录功能(与“ item_use_data”不同)。在开发人员端,它涉及更多的设置,但是,我认为,这是适用于需要使用关系数据库存储数据的情况的最优雅的解决方案。一般而言,非关系数据库在游戏开发方面要好得多,因为它们在数据建模方面更加灵活,同时比基于SQL的数据库更具性能-使它们成为“双赢”的选择。

编辑1:更正了potion_type_id字段的错误

编辑2:添加了有关非关系数据库和关系数据库的更多详细信息,以提供更多的视角


我认为该系统没有太大的灵活性。例如,如果我想要一个玻璃小瓶,既可以用作武器,又可以变得“锋利”…………我会很不幸。另外,对于每种新类型的项目,我都必须为其实际创建数据库结构。即使只有一种或两种类型。
卡扎伊2010年

您提出的观点是非常有效的,并突出说明了为什么关系数据库不适用于此特定情况的其他示例。我介绍的模型仅显示了以节省内存和保留SQL功能的方式对数据进行布局的最佳方法。为了存储多种类型的物品,在此模型下表示数据的最佳方法是创建一个具有适当的药水和武器属性的potion_weapon表。同样,此解决方案不是理想的解决方案,但它是使用关系数据库时最优雅的解决方案。
阿里·帕特里克

@Ari:没有无礼的意图,但是他的问题恰恰是为什么关系方法更有效,而不是更少。众所周知,NoSQL(或NoRel)数据库非常适合大批量读取,分布式/高可用性数据或定义不明确的模式。这种情况只是一个经典的多对多关联,Mongo之类的数据库并没有RDBMS那样好。参见stackoverflow.com/questions/3332093/…–
alphadogg,2010年

@alphadogg这是多对多关系吗?在数据库中,武器知道武器类型,但是武器类型不需要知道与武器相关的武器。如果出于某种原因您想了解特定武器类型的所有武器,则只需编写一个遍历武器文档集合的查询即可。
阿里·帕特里克

@阿里:在OP的问题中,一种武器可能有多种类型,而一种类型可能与许多武器有关。例如,药水和剑都是可以握的。剑既可握,又可武器。
alphadogg

3

首先,转储面向对象的继承方法,并使用基于组件的系统

完成此操作后,SQL布局突然变得容易得多。每个组件类型都有一个共享ID号的表。如果您要商品#17,则可以在每个表中查找“商品ID 17”。任何具有键的表都会添加其组件。

您的物品表包含一般物品的所有必需数据(名称,售价,重量,大小,所有物品之间共享的其他任何信息)。武器表包含武器的所有适当数据,药水表包含所有适当的数据对于药水,您的Armor表包含所有适当的装甲数据。想要一个胸甲,那是Item中的一个条目,而Armor中是一个条目。想要一个可以喝的剑hel,只需在每个桌子上放一个条目,然后就可以完成了。

请记住,这种模式并不是特定于物品的-您可以将其用于生物,区域以及其他任何您想要的东西。它具有惊人的多功能性。


2

使用SQL是您的严重错误。它绝对不适合存储静态游戏设计数据。

如果您无法摆脱SQL,我会考虑将项目存储在序列化自中。即

item_id (int) | item (BLOB)
1             | <binary data>

当然,这很丑陋,并把所有的SQL“ niceties”扔到了窗外,但是您实际上需要它们吗?最有可能的是,无论如何,您都可以在游戏开始时读取所有项目数据,而从来SELECT没有其他情况item_id


3
由于(根据我的理解)游戏是多人游戏并支持PvP,因此大多数游戏信息都可能没有存储在客户端上。如果这是正确的,则每次从表中读取数据时,都必须先对数据进行反序列化处理,然后再进行远程使用。结果,这种格式使得按属性查询项目非常昂贵。例如:如果要检索“武器”类型的所有项目,则必须检索每个项目,反序列化每个项目,然后手动筛选“武器”类型,而不是运行简单的查询。
阿里·帕特里克

1
根据我的经验,无论如何,所有项目都将在游戏开始时读入内存缓存中。因此,您仅在启动时反序列化它们一次,此后完全不使用数据库。如果在这种情况下不是这样,那么我同意,存储序列化对象是一个坏主意。
没关系,2010年

嗯,唯一的事情是,这给我留了一个-必需-接口来编辑数据,这很不方便。
卡扎伊2010年

2

根据您可能需要多少个特征,可以对具有与不同特征对应的不同位的对象使用简单的位掩码:

000001 = holdable
000010 = weapon
000100 = breakable
001000 = throwable
010000 = solids container
100000 = liquids container

然后,您可以进行简单的位测试,以查看对象是否可用于特定功能。

因此,“玻璃瓶”的值可能为101111,这意味着它可以握住,可以用作武器,容易破裂,可以扔掉,并且可以容纳液体。

您为项目创建的任何编辑器都可以具有一组简单的复选框,以启用/禁用对象的特征。


1
我喜欢这个主意,因为它允许某项保留其自身的特征,并且我不知道我需要在数据库中搜索这些特征,但是我不确定如何使它起作用在关系上下文中。我想每一位都将映射到数据库中特征的增量ID ...
Kzqai 2010年

我想位屏蔽对于固定数量的特征更有用,但是吗?
卡扎伊2010年

非常非常。如果您用完了最后一个“位”会怎样?增加数据类型以获取位并通过应用层进行渗透,或添加一列并执行相同操作,有多么容易?另外,您的特定数据库引擎是否针对位操作的索引进行了优化?最好是在关系数据库中保持数据列的原子性。不要将多个域合并为一列。您无法利用该平台的优势。
alphadogg,2010年

1
如果您想在数据库中查找有关特征的查找表(可能是个好主意,然后可以将其动态添加到任何编辑器中),那么它将仅具有以下内容:id,bitmask,description。1,1,“可持有”;2,2,“武器”;3,4,“可折断”等。对于固定数字,是的,这是正确的。但是,如果您使用64位int,则应该有足够的作用域。
Muttley

1

在我们的项目中,我们具有item_attributes,用于表示项目可能具有的不同“额外数据”。它的布局如下所示:

item_id | attribute_id    | attribute_value | order 
1        25 (attackspeed)       50             3    

然后我们有一个属性表,如下所示:

id |   name       | description
1    attackspeed    AttackSpeed:

但是,Ari Patrick是正确的,最终并不是为此创建关系数据库。缺点是我们有一些非常复杂的过程来生成新项目(通过外部工具完成-我强烈建议您这样做,请勿尝试手动添加这些项目,您只会感到困惑)

您拥有的另一个选项是使用脚本语言来创建项目模板,然后您可以轻松地解析其中的模板并将其用于创建新项目。当然,您仍然必须将项目数据保存在数据库中,但是此时,您无需担心创建新项目的细节,您几乎可以复制和删除旧脚本文件,更改一些信息,就可以了。去。

老实说,如果我们要重新创建新的静态项目,我们可能会使用脚本项目模板来采用一种更简单的方法。


1

这是典型的多对多关系,对于任何功能强大的关系数据库来说,都太深奥了。您对任何一个对象都有许多特征,并且一种或多种不同的对象类型可以使用任何一种特征。对三个关系(表)建模,其中一个是关联关系,您就完成了。正确的索引编制将确保您快速读取数据。

在代码中放置一个ORM,在数据库和中间件之间来回往返的问题很少。许多ORM本身也能够自动生成类,因此变得更加“不可见”。

至于NoSQL数据库,没有理由不能使用它们。目前,在贸易抹布和博客中为该技术加油助威是很时尚的,但是它们带有很多自身的问题:相对不成熟的平台,非常适合简单的不经常更改的读取(例如个人资料中的一对多twit),但是对于复杂的动态读取或更新,支持的工具链,冗余的数据以及随之而来的完整性问题等而言,效果不佳。但是,它们避免了不同程度的事务处理功能及其开销,并且简化了分发/复制模型,因此具有更好的可伸缩性吸引力。

关系数据库与NoSQL数据库之间的共同点是性能。不同的RDMBS涉及不同程度的开销,这使它们在扩展到Facebook或Twitter级别时不太受欢迎。但是,您不太可能会遇到这些问题。即使那样,基于简单SSD的服务器系统也可能使性能争论毫无用处。

让我们清楚一点:大多数NoSQL数据库从根本上来说都是散列表,并且将以与代码中的散列表相同的方式限制您,即。并非所有数据都完全适合该模型。关系模型对于建模数据之间的关系要强大得多。(令人困惑的因素是,大多数RDBMS是遗留系统,无法很好地满足流行的0.0001%Web的需求,即Facebook等)。


如果您要集体引用它们,甚至不用理会数据库。NoSQL并不是一个可以有意义地讨论的实体,它是SQL怪人为了应用有缺陷的逻辑而使用的无用质量参考,该逻辑是:如果此已定义组的一个数据库缺少功能,那么所有数据库都将缺少该功能。
aaaaaaaaaaaa

NoSQL是NoSQL推动者选择适应的术语。我不知道为什么,它不是很描述。但这不是贬义。经过与几个人的合作,我认为alphadogg对它们的描述是公平的。

也许我的评论有点刺耳,但是,我仍然讨厌这个名称和分组,当人们保持这种简化的世界观时,就不可能就不同数据库系统的更详细的细节进行有争议的辩论。
aaaaaaaaaaaa

@eBusiness:我认为您的评论很有趣,因为这是反RDBMS阵营(如果有的话)将其技术组自称为“ NoSQL”。诚然,事后看来,他们中的许多人希望看到这个名字被弃用。尤其是随着它们中的许多最终在其物理实现上分层SQL。至于谈论它们,我没有把您的评论视为苛刻,或者我的皮肤很厚,您可以选择!:)我的意思是,可以谈论一下茶话会,为什么不是NoSQL数据库组呢?
alphadogg

是的,我知道这种关系是一对多的。使我无法以真正的规范化样式创建它的原因是,认为组成一个项目的“东西”将至少分布在两个表中。我认为我不喜欢无法创建原子插入的想法。
Kzqai 2010年

0

这是我发现更现代的OR映射器真正有用的地方,例如Entity Framework 4及其(CTP)代码优先功能。您只需要用常规代码编写类及其关系,而无需修饰它们或手动运行任何工具,EF就会为您生成SQL格式的后备存储,并提供所需的链接表和所有...这确实释放了创意imo ^^


好吧,我将只首先创建代码和类以及所有这些代码,然后再将其转换为现有的数据库结构。
卡扎伊州2010年

0

这是我现在正在考虑的内容:

由于每个“特征”无论如何本质上都需要对代码进行更改,因此我决定仅将特征(以及它们所需的任何默认数据)保留在代码本身中(至少到目前为止)。

例如 $traits = array('holdable'=>1, 'weapon'=>1, 'sword'=>array('min_dam'=>1, 'max_dam'=>500));

然后,项目将在数据库中获取一个“ trait_data”字段,该字段将使用该json_encode()函数以JSON格式进行存储。

item_id | item_name | item_identity | traits
1 | Katana | katana | "{"holdable":1,"weapon":1,"sword":{"min_dam":1,"max_dam":1234,"0":{"damage_type":"fire","min_dam":5,"max_dam":50,"type":"thrown","bob":{},"ids":[1,2,3,4,5,6,7]}}}"

计划是,项目将从其父特征继承所有默认统计信息,并且仅具有覆盖特征,这些特征指定与特征设置为默认特征的差异。

traits字段的缺点是,虽然我可以手动编辑json部分... ...使用数据库中的数据进行操作确实不是那么容易或安全的。


1
引入新特性会发生什么?这种情况多久发生一次?对您的代码维护工作量有什么下游影响?与将其存储为多对多的方式形成对比(即,通过选择ORM动态构建对象实例以保存该数据)。
alphadogg

谢谢,这是一个好主意,添加新特征并不是一件容易的事。您已将分析瘫痪。:p
Kzqai

抱歉,但这很关键。您只需要仔细考虑。在需要对某些武器进行更新以添加新特性以使您的游戏增强功能正常工作时,您应该想到的锻炼模型。
alphadogg

-1

这有点古怪,我不能保证它是好的数据库设计。我的数据库类是前一阵子的,现在还没想到。但是,如果保证每个项目少于10个项目,则可以为每个项目指定一个attribute1字段和attribute2字段,依此类推,直到attribute10。这将消除您对多对多项目-属性表的需求,但需要限制属性的数量。

回顾这一点,我可以肯定它的设计很糟糕,但这确实意味着您可以坚持使用舒适的关系数据库,而不必进入未知领域。由您决定权衡是否值得。


这是可怕的设计,永远不要这样做。您最好不使用数据库。
ZorbaTHut

-1

Nevermind给出了一个很好的答案,但我想知道,您是否为此还需要数据库?将所有数据存储在一个纯文件中,并在启动时将其加载到程序中,这似乎是最明智的方法。

编辑:
对,在无状态请求驱动的环境中,您最好将数据保留在数据库中。将数据写入文件中,并编写一段代码以将其转换为Nevermind建议的类型的数据库。

或者,如果对象的数量不是太大,则在代码文件中按字面量声明批次可能是最快的方法。


嗯,我已经有一个数据库正在使用中并且已与该网站的其余部分集成。
卡扎伊2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.