Design patterns 具有动态字段的数据库设计:单表与多表-多索引
我必须选择一个数据库的结构,该结构将存储具有动态字段的内容类型(例如博客文章、页面、文档、发票、估价等):例如,Design patterns 具有动态字段的数据库设计:单表与多表-多索引,design-patterns,database-design,database-schema,Design Patterns,Database Design,Database Schema,我必须选择一个数据库的结构,该结构将存储具有动态字段的内容类型(例如博客文章、页面、文档、发票、估价等):例如,估价内容类型应包含标题、日期和总价 但是,在这段时间内,这些字段可以添加或删除,因此在1年后,估算持续类型可以有注释字段 这是著名的CMS(例如drupal)提供的一项常见任务,但我想知道获得最佳性能和灵活性的最佳方法是什么:例如,drupal使用一个带有basic字段(例如title)的表,所有辅助字段存储在动态创建的子表中,并使用外键链接到主表: table node | id |
估价
内容类型应包含标题
、日期
和总价
但是,在这段时间内,这些字段可以添加或删除,因此在1年后,估算
持续类型可以有注释
字段
这是著名的CMS(例如drupal)提供的一项常见任务,但我想知道获得最佳性能和灵活性的最佳方法是什么:例如,drupal使用一个带有basic
字段(例如title
)的表,所有辅助字段存储在动态创建的子表中,并使用外键链接到主表:
table node
| id | title | ...
| 1 | First example |
table fields_node_total_price
| id | node_id | value |
| 1 | 1 | 123.45 |
table fields_node_date
| id | node_id | value |
| 1 | 1 | 12345677 |
等等
我的观点是,这种方法非常灵活,但很容易陷入性能问题:为了获得文档的所有字段,必须多次连接表,代码本身必须多次迭代来构建查询(但这不应该是问题)
顺便说一句,多表是最常用的方法。。所以肯定有很多缺点
我在想,使用一张桌子会有什么好处:
| id | title | total_price | date | ec...
我用5个和50个附加字段做了一些测试;单表方法和多表方法之间的性能是惊人的:单表的速度大约快50倍
每次添加字段时,都会向表中添加一列。。这种方法会产生什么样的问题
编辑
让我提供一些细节:
Test 1° 2° 3° 4° 5° avg
1000 insert single_table 8,5687 8,6832 8,7143 8,7977 8,6906 8,69090137389466
1000 select single table LIKE '%key%' on char(250) field 1,5539 1,5540 1,5591 1,5602 1,5564 1,556705142
1000 select single table LIKE '%key%' on char(25) field 0,8848 0,8923 0,8894 0,8919 0,8888 0,889427996
1000 select single table id = $n 0,2645 0,2620 0,2645 0,2632 0,2636 0,263564462
1000 select single table integer field < $j 0,8627 0,8759 0,8673 0,8713 0,8767 0,870787334
1000 insert multi_table 446,3830 445,2843 440,8151 436,6051 446,0302 443,023531816
1000 select multi table LIKE '%key%' on char(250) field 1,7048 1,6822 1,6817 1,7041 1,6840 1,691367196
1000 select multi table LIKE '%key%' on char(25) field 0,9391 0,9365 0,9382 0,9431 0,9408 0,939536426
1000 select multi table id = $n 0,9336 0,9287 0,9349 0,9331 0,9428 0,93460784
1000 select multi table integer field < $j 2,3366 2,3260 2,3134 2,3342 2,3228 2,326600456
测试1°2°3°4°5°平均值
1000插入单_表85687 86832 87143 87977 86906 869090137389466
1000在字符(250)字段15539 15540 15591 15602 15564 1556705142上选择单个表,如“%key%”
1000在字符(25)字段08848 08923 08894 08919 08888 0889427996上选择单个表,如“%key%”
1000选择单个表id=$n 02645 02620 02645 02632 02636 0263564462
1000选择单表整数字段<$j 08627 08759 08673 08713 08767 0870787334
1000插入多表4463830 4452843 4408151 4366051 4460302 443023531816
1000在字符(250)字段17048 16822 16817 17041 16840 1691367196上选择多表,如“%key%”
1000在字符(25)字段09391 09365 09382 09431 09408 0939536426上选择多表,如“%key%”
1000选择多表id=$n 09336 09287 09349 09331 09428 093460784
1000选择多表整数字段<$j 23366 23260 23134 23342 23228 2326600456
第一种解决方案是“值属性”数据库:
我会选择后面的解决方案:数据库是用来存储数据而不是结构的!我们遇到了一个大问题,因为我们有一个实体-值-属性数据库,我们可以插入任何类型的数据,但如果没有神奇的字符串,就不可能查询它们或针对特定的数据
或者,您可以使用另一种解决方案:将additionnal字段存储在AdditionnalFields对象的序列化版本中。这个问题没有一个“正确”的答案。正如您已经提到的,它归结为灵活性和速度之间的权衡
这取决于应用程序中的瓶颈是什么。你对你的应用程序做了一些分析吗?数据库查询时间是否与典型的最终用户ping时间、传输速度等相关?在您确定确实存在性能问题并知道瓶颈在哪里之前,担心优化性能是毫无意义的
我喜欢使用Firefox上的firebug来计算我的页面与最终用户显示所需的时间,并将其与查询前启动、查询后停止的秒表计时器的结果进行比较。为了便于使用,我在评测期间将其打印在每页的底部
您是否考虑过视图来弥补多表方法的缺点
关于复杂的查询问题:使用“虚拟”视图,您可以避免在每天的查询中使用复杂的连接。将连接放置在视图定义中,更改动态字段时,只需调整视图即可。(注意:对于虚拟视图,使用视图定义中的联接动态重写“简单”查询。)
关于速度问题:您可以使用“物化”视图定义和多表方法来获得单表性能。对于物化视图,DBMS通过使用视图定义中的联接,使用视图定义创建物理表。结果是您真正查询的是一个“单表”——但它会自动与多表定义保持同步。您以牺牲DB存储空间为代价,实现了两全其美
根据您的DBMS,您还可以直接更新视图(而不是多表)。我相信MySQL就是这样。对于Postgres,您需要使用触发器来告诉系统如何修改底层多表
CREATE TABLE [dbo].[ParentTbl](
[Id] [int] IDENTITY(1,1) NOT NULL,
[KnownCol1] [real] NOT NULL,
-- Lots of other columns ommitted
[KnownColn] [real] NULL
)
CREATE TABLE [dbo].[MainTbl](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NOT NULL, -- FK to ParentTbl.Id
[KnownCol1] [real] NOT NULL,
-- Lots of other columns ommitted
[KnownColn] [real] NULL
)
CREATE TABLE [dbo].[MainTblAttr](
[Id] [bigint] IDENTITY(1,1) NOT NULL, -- Note big int to cater for LOTS of records
[MainId] [int] NOT NULL, --FK to MainTbl.Id
[AttributeColumn] [nvarchar](255) NOT NULL,
[AttributeValue] [nvarchar](max) NOT NULL
)