关于sql:动态数据库架构

关于sql:动态数据库架构

Dynamic Database Schema

为动态逻辑数据库架构提供存储的推荐体系结构是什么?

需要说明的是:在要求系统为模型提供存储的情况下,其模型可能会在生产后由其用户扩展或更改,那么哪些好的技术,数据库模型或存储引擎将允许这样做?

一些可能性来说明:

  • 通过动态生成的DML创建/更改数据库对象
  • 创建具有大量稀疏物理列的表,并且仅使用"重叠"逻辑架构所需的表
  • 创建一个"长而窄"表,将动态列值存储为行,然后需要对其进行透视处理以创建一个"短而宽"行集,其中包含特定实体的所有值
  • 使用BigTable / SimpleDB PropertyBag类型系统

任何基于现实世界经验的答案将不胜感激


您的建议并不新鲜。很多人都尝试过……大多数人发现他们追求"无限"的灵活性,而最终获得的收益却远不止于此。这是数据库设计的"蟑螂汽车旅馆"-数据可以输入,但几乎不可能将其导出。尝试并针对任何类型的约束对代码进行概念化编写,您将明白我的意思。

最终结果通常是一个更难以调试,维护和充满数据一致性问题的系统。情况并非总是如此,但通常情况是这样的。主要是因为程序员看不到火车残骸的到来,并且无法防御性地针对它进行编码。而且,通常最终会出现这样的情况:"无限"的灵活性实际上不是必需的。当开发团队得到一个说明说"天哪,我不知道他们要把什么样的数据放在这里,所以让它们放到哪里"时,这是一个非常糟糕的"气味"……而最终用户也很好具有可以使用的预定义属性类型(编写通用电话号码,并让他们创建其中任何一个,这在一个很好的标准化系统中并不重要,并且可以保持灵活性和完整性!)

如果您有一支非常优秀的开发团队,并且充分意识到使用此设计必须克服的问题,那么您可以成功编写出设计良好,而不是令人毛骨悚然的系统的代码。大多数时候。

但是,为什么为什么要从与您对立的赔率开始呢?

不相信我吗Google"一个真实的查询表"或"单个表设计"。一些好的结果:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0:::::P11_QUESTION_ID:10678084117056

http://thedailywtf.com/Comments/Tom_Kyte_on_The_Ultimate_Extensibility.aspx?pg=3

http://www.dbazine.com/ofinterest/oi-articles/celko22

http://thedailywtf.com/Comments/The_Inner-Platform_Effect.aspx?pg=2


MSSQL中的强类型xml字段对我们有用。


就像其他人所说的那样,除非您别无选择,否则不要这样做。要求这样做的一种情况是,您要销售必须允许用户记录自定义数据的现成产品。我公司的产品属于这一类。

如果确实需要允许您的客户这样做,这里有一些提示:
-创建强大的管理工具来执行模式更改,并且不允许以任何其他方式进行这些更改。
-使其具有管理功能;不允许普通用户访问它。
-记录有关每个架构更改的每个详细信息。这将帮助您调试问题,并且如果客户做一些愚蠢的事情,还将为您提供CYA数据。

如果您可以成功完成这些操作(尤其是第一个操作),那么您提到的任何体系结构都将起作用。我的首选是动态更改数据库对象,因为当您访问存储在自定义字段中的数据时,这使您可以利用DBMS的查询功能。其他三个选项要求您加载大量数据,然后在代码中执行大部分数据处理。


我有类似的要求,因此决定使用无模式的MongoDB。

MongoDB (from"humongous") is an open source, scalable, high-performance, schema-free, document-oriented database written in the C++ programming language. (Wikipedia)

强调:

  • 具有丰富的查询功能(也许是最接近SQL数据库的数据库)
  • 生产就绪(foursquare,sourceforge使用它)

Lowdarks(您需要了解的东西,因此您可以正确使用mongo):

  • 没有交易(实际上有交易,但仅在原子操作上)
  • 这个东西在这里:http://ethangunderson.com/blog/two-reasons-to-not-use-mongodb/
  • 耐久性..主要是与酸有关的东西

我在一个真实的项目中做到了:

该数据库由一个表和一个字段组成,其中一个字段由50个数组组成。该字段上设置了"单词"索引。所有数据都是无类型的,因此"单词索引"按预期工作。数字字段用字符表示,实际排序已在客户端完成。 (如果需要,每种数据类型仍然可以具有多个数组字段)。

逻辑表的逻辑数据模式保存在同一数据库中,但表行"类型"不同(第一个数组元素)。它还支持使用相同的"类型"字段以写时复制样式进行的简单版本控制。

好处:

  • 您可以动态地重新排列和添加/删除列,而无需转储/重新加载数据库。任何新的列数据都可以在零时间内设置为(虚拟)初始值。
  • 由于所有记录和表的大小均相同,因此碎片最少,有时性能会更好。
  • 所有表架构都是虚拟的。任何逻辑模式结构都是可能的(甚至是递归的或面向对象的)。
  • 这对于"一次写入,大部分读取,无删除/标记为删除"的数据很有用(大多数Web应用程序实际上就是这样)。
  • 缺点:

  • 仅按完整词编制索引,无缩写,
  • 可能进行复杂的查询,但性能会稍有下降。
  • 取决于您的首选数据库系统是否支持数组和单词索引(在PROGRESS RDBMS中进行了补充)。
  • 关系模型仅在程序员心目中(即仅在运行时)。
  • 现在我在想下一步可??能是-在文件系统级别上实现这样的数据库。那可能比较容易。


    建立关系数据库的全部目的是保持数据安全和一致。从允许用户更改架构的那一刻起,您的数据完整性便随之而来...

    如果您需要存储异构数据(例如CMS场景),则建议连续存储经过XSD验证的XML。当然,您会失去性能和简单的搜索功能,但这是对IMHO的一个很好的权衡。

    既然是2016年,那就不用XML了!使用JSON来存储非关系数据包,并使用适当类型的列作为后端。通常,您不需要在包中按值查询,这会很慢,即使许多现代SQL数据库本机都可以理解JSON。


    我知道问题中指出的模型已在整个生产系统中使用。我供职的大型大学/教学机构正在使用一个相当大的应用程序。他们专门使用狭长表方法来映射由许多不同的数据采集系统收集的数据。

    此外,Google最近通过其代码站点发布了其内部数据共享协议(协议缓冲区)作为开源。以这种方法为模型的数据库系统将非常有趣。

    检查以下内容:

    实体-属性-价值模型

    Google协议缓冲区


    在我看来,您真正想要的是某种"元模式",这是一种数据库模式,能够描述用于存储实际数据的灵活模式。动态模式更改是敏感的,不是您想惹的麻烦,尤其是在允许用户进行更改的情况下。

    您不会找到比其他任何数据库更适合此任务的数据库,因此,最好的选择就是根据其他条件选择一个。例如,您使用什么平台托管数据库?应用程序用什么语言编写?等等

    为了澄清我的意思是"元模式":

    1
    2
    3
    4
    5
    6
    7
    CREATE TABLE DATA (
        id INTEGER NOT NULL AUTO_INCREMENT,
        KEY VARCHAR(255),
        DATA TEXT,

        PRIMARY KEY (id)
    );

    这是一个非常简单的示例,您可能会根据自己的需求找到一些更具体的东西(希望可以更轻松地使用它),但这确实可以说明我的观点。您应该认为数据库模式本身在应用程序级别是不可变的。任何结构上的更改都应反映在数据中(即该架构的实例化)。


    创建2个数据库

    • DB1包含静态表,并表示数据的"真实"状态。
    • DB2是免费的,用户可以根据自己的意愿进行操作-他们(或您)将不得不编写代码以从DB1填充其奇数形的表。

    我相信EAV方法是最好的方法,但是成本很高


    我知道这是一个古老的话题,但是我想它永远不会失去现实性。
    我现在正在开发类似的东西。
    这是我的方法。
    我将服务器设置与MySQL,Apache,PHP和Zend Framework 2用作应用程序框架,但它应与其他设置一起使用。

    这是一个简单的实施指南,您可以据此进一步发展。

    您将需要实现自己的查询语言解释器,因为有效的SQL太复杂了。

    例:

    1
    SELECT id, password FROM USER WHERE email_address ="xyz@xyz.com"

    物理数据库布局:

    表"规格" :(应缓存在数据访问层中)

    • id:int
    • parent_id:整数
    • 名称:varchar(255)

    表"项目":

    • id:int
    • parent_id:整数
    • spec_id:整数
    • 数据:varchar(20000)

    表" specs"的内容:

    • 1,0,'用户'
    • 2,1,'email_address'
    • 3、1,"密码"

    表" items"的内容:

    • 1,0,1,''
    • 2,1,2,'xyz@xyz.com'
    • 3、1、3,"我的密码"

    用我们自己的查询语言翻译示例:

    1
    SELECT id, password FROM USER WHERE email_address ="xyz@xyz.com"

    标准SQL看起来像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    SELECT
        parent_id, -- user id
        DATA -- password
    FROM
        items
    WHERE
        spec_id = 3 -- make sure this is a 'password' item
        AND
        parent_id IN
        ( -- get the 'user' item to which this 'password' item belongs
            SELECT
                id
            FROM
                items
            WHERE
                spec_id = 1 -- make sure this is a 'user' item
                AND
                id IN
                ( -- fetch all item id's with the desired 'email_address' child item
                    SELECT
                        parent_id -- id of the parent item of the 'email_address' item
                    FROM
                        items
                    WHERE
                        spec_id = 2 -- make sure this is a 'email_address' item
                        AND
                        DATA ="xyz@xyz.com" -- with the desired data value
                )
        )

    您将需要将规范表缓存在关联数组或哈希表中或类似的东西中,才能从规范名称中获取spec_id。否则,您将需要插入更多的SQL开销才能从名称中获取spec_id,如以下代码片段所示:

    不好的例子,不要使用它,避免使用它,而是缓存规格表!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    SELECT
        parent_id,
        DATA
    FROM
        items
    WHERE
        spec_id = (SELECT id FROM specs WHERE name ="password")
        AND
        parent_id IN (
            SELECT
                id
            FROM
                items
            WHERE
                spec_id = (SELECT id FROM specs WHERE name ="user")
                AND
                id IN (
                    SELECT
                        parent_id
                    FROM
                        items
                    WHERE
                        spec_id = (SELECT id FROM specs WHERE name ="email_address")
                        AND
                        DATA ="xyz@xyz.com"
                )
        )

    我希望您能想到这个想法,并可以自己确定该方法是否对您可行。

    请享用! :-)


    在c2.com Wiki上,探讨了"动态关系"的概念。您不需要DBA:列和表都是写时创建的,除非您开始添加约束以使其表现得更像传统RDBMS:随着项目的成熟,您可以逐步"锁定"它。

    从概念上讲,您可以将每一行都视为XML语句。例如,员工记录可以表示为:

    1
     

    这并不意味着必须将其实现为XML,这只是一个方便的概念化。如果您请求一个不存在的列,例如" SELECT madeUpColumn ...",则将其视为空白或空值(除非添加了约束禁止这样做)。可以使用SQL,尽管由于隐含的类型模型,在比较时必须小心。但是,除了类型处理之外,动态关系系统的用户会感到宾至如归,因为他们可以利用他们现有的大多数RDBMS知识。现在,如果有人愿意建造它...


    我知道这是一篇过时的文章,并且在过去11年中发生了很多变化,但是我想添加一下,因为这可能对将来的读者有所帮助。我和我的共同创始人创建HarperDB的原因之一是在提供完整索引功能的同时,在单个无重复的数据集中本地完成Dynamic模式。您可以在此处了解更多信息:
    https://harperdb.io/blog/dynamic-schema-the-harperdb-way/


    ElasticSearch。特别是在处理可以按日期分区的数据集,可以对数据使用JSON以及不固定使用SQL检索数据时,应该特别考虑这一点。

    ES为您发送的任何新JSON字段(自动使用提示)或您可以通过一个HTTP命令("映射")定义/更改的方式推断模式。
    尽管它不支持SQL,但它具有一些强大的查找功能,甚至可以聚合。


    过去,我选择了选项C-创建一个"长而狭窄"的表,将动态列值存储为行,然后需要对其进行透视处理以创建一个"短而宽"的行集,其中包含特定实体的所有值。但是,我使用的是ORM,这确实使事情很痛苦。我想不出如何在LinqToSql中做到这一点。我想我必须创建一个哈希表来引用字段。

    @Skliwz:我猜他对允许用户创建用户定义的字段更感兴趣。


    sql已经提供了一种更改架构的方法:ALTER命令。

    只是有一个表列出了不允许用户更改的字段,并为ALTER编写了一个不错的界面。


    推荐阅读