在数据库表中指示具有特殊处理的值的最佳方式是什么



设置

我有一张表,里面存储了一个游戏的实物清单。项目还具有类别的层次列表。示例基表:

项目

id | parent_id | is_category | name    | description  
-- | --------- | ----------- | ------- | -----------  
1  | 0         | 1           | Weapon  | Something intended to cause damage
2  | 1         | 1           | Ranged  | Attack from a distance
3  | 1         | 1           | Melee   | Must be able to reach the target with arm
4  | 2         | 0           | Musket  | Shoots hot lead.
5  | 2         | 0           | Bomb    | Fire damage over area
6  | 0         | 1           | Mount   | Something that carries a load.
7  | 6         | 0           | Horse   | It has no name.
8  | 6         | 0           | Donkey  | Don't assume or you become one.

该系统目前运行在PHP和SQLite上,但数据库后端很灵活,可能使用MySQL,前端最终可能使用javascript或Object-C/Swift

问题

在上面的示例中,程序必须对每个顶级类别及其下面的项目有不同的特殊处理。例如,武器和坐骑由不同的商人出售,武器可以携带,而坐骑不能携带。

在代码中标记顶级层以进行特殊处理的最佳方式是什么?

  • 虽然顶级类别相对固定,但我希望将它们保留在数据库中,这样使用单个(递归)函数更容易生成可视化的完整层次结构
  • 几乎所有标识项目的外键都可能标识项目类别,因此将它们划分为不同的表似乎非常笨拙

我的想法

  1. 我可以在名称上使用字符串匹配,并在第一次执行时将id存储在内部常量中。这充其量是一个我想避免的丑陋的解决方案
  2. 我可以在安装时将id存储在内部常量中。更好,但仍然不是我喜欢的
  3. 我可以将数组存储在顶级元素的代码中,而不是将它们放在表中。这会产生很多复杂的问题,比如孩子如何指向顶级父母。另一个id必须添加到10K行中大约100行使用的表中
  4. 我可以将数组存储在代码中,并在安装时启用标识插入,以添加共享静态数组标识的顶级元素。这可能是我最好的主意,但我真的不喜欢身份插入的想法,它对我来说只是感觉不到"数据库"。此外,如果出现新的顶级项目会怎么样。也许这些类别的ID从100万开始
  5. 我可以添加一个标志列"varchar(1)top_category"或"int-top_category",其中包含指示值的字符或位图。同样,一列用于10k行中的10行

作为一名软件人员,我倾向于完善软件解决方案,所以我很好奇他们是否是一个更DB类型的解决方案。

原始表,带有操作的联接。

是的,你可以把所有东西都放在一张桌子上。您只需要为每个场景建立唯一的行。这个sqlfiddle给你举了一个例子。。。但是IMO它开始变得难以理解。这并不能照顾到所有的场景,因为无法进行完全连接(这只是sqlfiddle的一个限制,在其他方面非常棒。)

IMO,将事物分解成表格更有意义。下面是我如何开始为您描述的一些场景进行模式设计的另一个例子。

基表本身看起来很笨重,但它在如何使用数据方面提供了更大的灵活性。

tl;dr前面的类比

数据集不是按行组织的服装列表。这是你用来存放构成一套服装的小床的地方。

因此,将事物分解成单独的表的笨拙感觉,实际上是关系数据库的好处。把所有东西都放在一张表中,一开始感觉很高效,也很优化。。。但随着复杂性的扩大。。。它开始变得痛苦。

把你的模式想象成一个梳妆台。抽屉就是你们的桌子。如果你只有几只袜子和内衣,那么把它们都放在一个抽屉里是很有效的。但一旦你有了足够的袜子,把它们和内衣放在同一个抽屉里会很痛苦。你有连衣裙袜,船员袜,脚踝袜,毛茸茸的袜子。所以你把它们放在另一个抽屉里。一旦你有了衬衫、短裤、裤子,你也开始把它们放在抽屉里。

将所有数据放入单个表的驱动器通常由您打算如何使用数据来驱动。

假设你的梳妆台存货充足,井然有序,你就有了几套潜在的独特服装;整齐地摆放在你的梳妆台上。你只需要把它们放在一起。Select和Joins就是你要组装的服装。事实上,你最喜欢的牛仔裤/t恤/袜子组合并不是放在一个抽屉里,这并不会让它变得笨重或低效。它们是分离和组织的,这一事实使您能够:1.快速知道在哪里可以买到每件物品2.查看潜在的其他最受欢迎的组合3.快速查看你的装备的每个组件

选择先考虑服装,然后再考虑以后的收纳方式并没有错。如果你只有一套衣服,把所有的东西都放在一个抽屉里比把每个馅饼都放在单独的抽屉里容易得多。然而,当你扩大衣柜时,放所有东西的单一抽屉开始变得低效。

您通常希望规划扩展和多功能性。您的程序可以根据需要将数据组合在一起。一个组织良好的架构可以为您做到这一点。是否使用ORM并进行模型驱动的数据存储;或者从模式开始,然后基于该模式构建模型;数据需求变得越复杂;两种方法变得越相似。

关系数据库旨在将实体存储在相互关联的表中。你经常会看到由部门、员工、工作等组成的公司数据库的例子,或者持有产品、客户、订单和供应商的商店的例子。

查询这样的数据库非常容易,例如,获取在特定部门有特定工作的所有员工:

select *
from employees
where job_id = (select id from job where name = 'accountant')
and dept_id = select id from departments where name = 'buying');

另一方面,您只有一个包含"事物"的表。一行可以与另一行相关,意思是"属于类型"。你可以称这张桌子为"什么"。如果是关于公司数据,我们就会得到这份工作:

select * 
from something
where description = 'accountant'
and parent_id = (select id from something where description = 'job');

因此,该部门:

select * 
from something
where description = 'buying'
and parent_id = (select id from something where description = 'department');

这两者仍然必须由某个部门的工作人员联系起来。那么,仅仅"是类型"是不够的。我上面显示的简短查询对于您的数据库类型来说会变得相当大和复杂。想象一下,对于更复杂的查询也是如此。

你的应用程序要么根本不知道它在选择什么(好吧,它知道这是某种类型的东西,另一种是某种类型,而且这个人(如果你甚至介绍了一个个人表)以某种方式与这两件事有联系),要么它必须知道"部门"的描述是什么意思,"工作"的描述意味着什么。

你的数据库是盲的。它不知道什么是"某物"。如果你在某个时候犯了一个编程错误(我们大多数人都会这样做),你甚至可能存储错误的关系(驴是Musket类型的,因此在你可以骑它的时候"发射热引线"),你的应用程序可能会在某个时刻崩溃,无法处理查询结果。

你不想让你的应用程序知道什么是武器,什么是支架吗?武器能让你战斗,坐骑能让你旅行?那么,为什么要保密呢?你认为你获得了灵活性吗?那么,在不更改应用程序的情况下,将食物添加到您的餐桌上。应用程序将如何处理这些信息?你看,无论如何你都必须编码这个。

实体数据分离。到目前为止,你的实体是武器和坐骑。这些应该是表格。然后,这些实体的实例(行)具有某些属性。例如,炸弹是一种具有一定射程的武器。

表格可能是这样的:

  • 人(person_id,name,strength_points,…)
  • 武器(weapon_id,name,range_from,range_to,weight,force_points,…)
  • person_weapon(person_id,weapon_id)
  • mount(mount_id、名称、速度、续航时间…)
  • person_mount(person_id,mount_id)
  • 食物(food_id、名称、重量、能量点…)
  • person_food(person_id,food_id)
  • armor(armor_id,name,protection_points,…)
  • person_armor<=一张用于m:n或一个人的表。id_armor用于1:n

这只是一个例子,但它清楚地显示了您的应用程序正在处理哪些实体。它知道武器和食物是一个人携带的东西,所以这些东西对一个人来说只能有最大的总重量。支架是一种用于运输的东西,可以让人移动得更快(或者负重,如果你的应用程序和桌子允许的话)。等等

最新更新