“水平”与“垂直”表设计,SQL

时间:2012-02-13 07:19:06

标签: php mysql database

道歉,如果过去已经彻底涵盖了这一点 - 我已经看过一些相关的帖子,但没有找到任何令我满意的特定情况。

我最近一直在寻找一个相对简单的游戏,大约有1万名玩家。在游戏中,你可以捕捉和繁殖具有某些属性的宠物(即翅膀,角,鬃毛)。目前数据库中有一个表格如下所示:

-------------------------------------------------------------------------------
| pet_id | wings1 | wings1_hex | wings2 | wings2_hex | horns1 | horns1_hex | ...
-------------------------------------------------------------------------------
|      1 |      1 |     ffffff |   NULL |       NULL |      2 |     000000 | ...
|      2 |   NULL |       NULL |   NULL |       NULL |   NULL |       NULL | ...
|      3 |      2 |     ff0000 |      1 |     ffffff |      3 |     00ff00 | ...
|      4 |   NULL |       NULL |   NULL |       NULL |      1 |     0000ff | ...
etc...

这个表继续这样,目前有100多列,但一般来说,一只宠物只有大约1-8个属性。每1-2个月添加一个新属性,这需要添加表列。该表很少更新并经常阅读。

我一直在提议我们采用更垂直的设计方案以提高灵活性,因为我们希望将来开始添加更多的属性,即:

----------------------------------------------------------------
| pet_id | attribute_id | attribute_color | attribute_position |
----------------------------------------------------------------
|      1 |            1 |          ffffff |                  1 |  
|      1 |            3 |          000000 |                  2 |  
|      3 |            2 |          ffffff |                  1 |  
|      3 |            1 |          ff0000 |                  2 |  
|      3 |            3 |          00ff00 |                  3 |  
|      4 |            3 |          0000ff |                  1 | 
etc...

旧开发人员担心这会产生性能问题,因为用户经常搜索具有特定属性的宠物(即必须具有这些属性,必须至少有一个此颜色或位置,必须具有> 30个属性) 。目前搜索速度非常快,因为不需要JOINS,但引入垂直表可能意味着搜索的每个属性都会有一个额外的连接,也会使行数增加三倍。

我的问题的第一部分是,是否有人对此提出任何建议?我对数据库设计或优化并不是特别熟悉。

我已经针对各种情况进行了测试,但它们在很大程度上尚无定论 - 我运行的所有查询(即半秒到20秒之间)的时间变化非常大,所以我想我的问题的第二部分是,在PHP中使用microtime(true)是否有更可靠的分析查询时间的方法。

感谢。

5 个答案:

答案 0 :(得分:17)

这称为Entity-Attribute-Value-Model,关系数据库系统根本不适合它。

引用认为是五个errors not to make之一的人:

  

那么为EAV吹捧的好处是什么?好吧,没有。由于EAV表将包含任何类型的数据,因此我们必须使用适当的列将数据PIVOT到表格表示,以使其有用。在许多情况下,有中间件或客户端软件在幕后执行此操作,从而为用户提供他们处理精心设计的数据的错觉。

     

EAV模型存在许多问题。

     

首先,大量数据本身基本上无法管理。

     

其次,没有可能的方法来定义必要的约束 - 任何潜在的检查约束都必须包括对适当属性名称的广泛硬编码。由于单个列包含所有可能的值,因此数据类型通常为VARCHAR(n)。

     

第三,甚至不要考虑使用任何有用的外键。

     

最后,查询的复杂性和尴尬性。有些人认为在必要时能够将各种数据插入单个表中是有益的 - 他们称之为“可扩展”。实际上,由于EAV将数据与元数据混合在一起,即使是出于简单的要求,操作数据也要困难得多。

     

EAV噩梦的解决方案很简单:分析和研究用户的需求并预先确定数据需求。关系数据库维护数据的完整性和一致性。如果没有明确定义的要求,设计这样的数据库几乎是不可能的。周期。


  

这个表继续这样,目前有100多列,但一般来说,一只宠物只有大约1-8个属性。

看起来像规范化的情况:将表分成多个,例如一个用于喇叭,一个用于翅膀,所有都通过外键连接到主实体表。但是请确保每个属性仍然映射到一个或多个列,以便您可以定义约束,数据类型,索引等。

答案 1 :(得分:0)

加入。该数据库专门用于支持用例的连接。如果有任何疑问,那么基准。

编辑:分析查询的更好方法是直接在CLI上的MySQL解释器中运行查询。它将为您提供运行查询所需的确切时间。 PHP microtime()函数还将引入其他延迟(Apache,PHP,服务器资源分配,网络,如果连接到远程MySQL实例等)。

答案 2 :(得分:0)

您提出的建议称为“normalization”。这正是关系数据库的用途 - 如果您处理索引,连接的运行速度几乎与数据在一个表中的速度一样快。

实际上,它们甚至可能更快:您可以只加载所需的列,而不是加载1个包含100列的表格行。如果宠物只有8个属性,则只加载8个。

答案 3 :(得分:0)

这个问题非常主观。如果您有资源更新中间件以反映已添加的列,那么无论如何,使用水平方向,没有什么比固定结构更安全,更容易学习。有一点需要记住,无论何时更新表结构,您都必须更新其中的每个依赖项,除非有一些类似*的全部内容,我建议您留意,除非您只是将数据转储到屏幕并且列的顺序是无关紧要的。

话虽如此,如果您没有满足所有要求或者不想在n个区域更新代码,Verticle就是您的选择。大多数情况下,您只需要存储容器来存储数据。我会在单独的列中分隔数字,日期,二进制和文本等内容以保留一些数据完整性,但Verticle存储没有任何问题,只要您知道如何制定和构造查询以将数据恢复到适当的位置。格式。

仅供参考,Wordpress使用Verticle数据存储来存储它必须存储的大部分动态内容,以用于数百万用途。

答案 4 :(得分:-2)

从数据库的角度来看,第一件事就是你的数据应该垂直增长而不是横向增长。因此,添加新列并不是一个好的设计。第二,这是数据库设计中非常常见的情况。要解决这个问题的方法,你必须创建三个表。第一个是Pets,第二个是属性,第三个是两个之间的映射表。这是一个例子:

表1(宠物)
Pet_ID | Pet_Name
1 |狗
2 |猫

表2(属性)
Attribute_ID | Attribute_Name
1 |翅膀
2 |眼睛

表3(Pet_Attribute)
Pet_ID | Attribute_ID | Attribute_Value
  1 | 1 | 0
 1 | 2 | 2

关于效果
Pet_ID和Attribute_ID是索引的主键(http://developer.mimer.com/documentation/html_92/Mimer_SQL_Engine_DocSet/Basic_concepts4.html),因此搜索速度非常快。这是解决问题的正确方法。希望,现在你会很清楚。