如何设计一个布局可能随时间变化的通用数据库?
Posted
技术标签:
【中文标题】如何设计一个布局可能随时间变化的通用数据库?【英文标题】:How to design a generic database whose layout may change over time? 【发布时间】:2011-03-01 23:20:50 【问题描述】:这是一个棘手的问题 - 我如何以编程方式创建和查询一个我无法真正预见其内容的数据库?
我正在实现一个通用输入表单系统。用户可以创建具有所见即所得布局的 php 表单,并将它们用于他希望的任何目的。他还可以查询输入。
所以,我们分为三个阶段:
-
设计并生成了一个表单。这是一个一次性的过程,尽管以后可以编辑表单。这设计了数据库。
有人或几个人使用该表单 - 例如用于每日销售报告、库存管理、工资单等。他们对表单的输入被写入数据库。
其他人(可能是管理层)可以查询数据库并生成报告。
由于这些表单是通用的,我无法预测数据库结构 - 只能说它将反映 html 表单字段并包含来自编辑框、备忘录、单选按钮等集合的数据输入。
问题和评论:
A) 就表和列而言,我怎样才能最好地构建数据库?主键呢?我的第一个想法是使用控件名称来标识每一列,然后我意识到用户可以编辑表单并重命名,这样可能“name”变成“employee”或者“wages”变成“:salary”。我倾向于为每个人设置一个唯一编号。
B) 如何最好地键入行?我正在考虑允许我查询的时间戳以及来自 A) 的行 ID 的列
C) 我必须处理列重命名/插入/删除。敌人删除,我不确定是否要从数据库中删除数据。即使用户不再从表单输入它,他也可能希望查询之前输入的内容。或者可能有一些法律要求来保留数据。列重命名/插入/删除中的任何陷阱?
D) 对于查询,我可以让我的 PHP 查询数据库以获取列名并生成一个带有列表的表单,其中每个条目都有一个数据库列名,一个复选框来说明是否应该在查询中使用它和,基于列类型,一些选择标准。这应该足以建立像“职位 = '高级推销员'和薪水 > 50k”这样的搜索。
E) 我可能需要生成一些花哨的图表——图形、直方图、饼图等,用于随着时间的推移查询数值数据的结果。我需要为此找到一些好的 FOSS PHP。
F) 我还忘记了什么?
这一切对我来说似乎很棘手,但我是数据库 n00b - 也许对你们大师来说很简单?
编辑:请不要告诉我不要这样做。我别无选择:-(
编辑:在现实生活中,我不希望列重命名/插入/删除频繁。但是,运行几个月后,可能需要对数据库进行更改。我确信这种情况经常发生。我担心我对这个问题的措辞很糟糕,人们会认为每 10 分钟左右就会不择手段地做出改变。
实际上,我的用户在布置表单时会定义一个数据库。他们可能第一次就做对了,而且永远不会改变它——特别是如果他们是从纸质表格转换而来的。即使他们决定改变,这也可能只会在几个月或几年之后发生一次或两次——这可能发生在任何数据库中。
我不认为我在这里有特殊情况,也不认为我们应该专注于改变。也许更好地专注于链接 - 什么是好的主键方案?比如说,对于一个文本输入、一个数字和一个备忘录?
【问题讨论】:
即使您在这件事上别无选择,但了解您正在进入的领域可能会有所帮助,这就是内部平台效应 (en.wikipedia.org/wiki/Inner-platform_effect) 的一个案例。您实际上是在制作一个元系统,以使用户能够完成程序员应该做的工作。祝你好运。 :) 如果这个项目成功了,就不需要别的了吗? 【参考方案1】:“这一切对我来说似乎很棘手,但是 我是数据库 n00b - 也许是 大师们简单吗?”
不,这真的 很棘手。从根本上说,您所描述的不是数据库应用程序,而是数据库应用程序builder。事实上,听起来好像您想编写类似Google App Engine 或 Web 版本的 MS Access 的代码。编写这样一个工具需要很多时间和专业知识。
Google 使用其 BigTable 平台实施了灵活的架构。它允许您随意调整模式。问题是,这种灵活性使得编写“position = 'senior salesman' and Salary > 50k”这样的查询变得非常困难。
所以我认为 NoSQL 方法不是您所需要的。您想要构建一个生成和维护 RDBMS 模式的应用程序。这意味着您需要设计一个元数据存储库,您可以从中生成动态 SQL 来构建和更改用户的模式并生成前端。
您的元数据架构需要存储的内容
对于模式生成:
外键关系(员工在部门工作) 唯一的业务密钥(只能有一个名为“销售”的部门) 参考数据(EMPLOYEE.POSITION 的允许值) 列数据类型、大小等 列是否可选(即 NULL 或 NOT NULL) 复杂的业务规则(员工奖金不能超过工资的15%) 列的默认值用于前端生成
显示名称或标签(“工资”、“薪水”) 小部件(下拉列表、弹出式日历) 隐藏字段 派生字段 帮助文本、提示 客户端验证(关联的 javascript 等)最后一点指出了您的提案中潜在的复杂性:像 Joe Soap 这样的常规表单设计师将无法制定 JS 来(比如说)验证输入值是否介于 X 和 Y 之间,因此您需要必须使用模板化规则来派生它。
这些绝不是详尽的清单,只是我的想法。
对于主键,我建议您使用 GUID 数据类型的列。不能保证时间戳是唯一的,但如果您在运行到六个位置(即不是 Windows)的操作系统上运行数据库,则不太可能发生冲突。
最后一句话
'我的第一个想法是使用 控件名称来标识每一列, 然后我意识到用户可以编辑 表单和重命名,这样也许 “姓名”变成“雇员”或“工资” 变成“:薪水”。我在倾斜 每个人都有一个唯一的号码。'
我以前构建过数据库模式生成器。他们很难。可能很难的一件事是调试动态 SQL。所以让自己更轻松:对表和列使用真实名称。仅仅因为应用程序用户现在想要查看标题为 HEADCOUNT 的表单并不意味着您必须重命名 EMPLOYEES 表。因此需要将显示的标签与模式对象名称分开。否则,您会发现自己试图找出生成的 SQL 语句失败的原因:
update table_11123
set col_55542 = 'HERRING'
where col_55569 = 'Bootle'
/
那是疯狂所在。
【讨论】:
【参考方案2】:本质上,您是在问如何在没有规范的情况下构建应用程序。关系数据库的设计不是为了让您可以有效地做到这一点。解决这个问题的常用方法是实体-属性-值设计,对于您想要使用它的系统类型,失败的几率几乎是 100%。
例如,名为“姓名”的列可能变成“工资”是没有意义的。如果工资值可以有“Fred”、“Bob”、100K、1000、“a lot”,那么您想要总工资的报告将如何工作?数据库并非旨在让任何人在任何地方放置任何东西。成功的数据库模式需要结构,这意味着在规范需要存储什么以及为什么存储方面付出努力。
因此,为了回答您的问题,我会重新考虑这个问题。尝试制作一个可以存储宇宙中任何东西的应用程序的整个方法并不是成功的秘诀。
【讨论】:
“询问如何在没有规范的情况下构建应用程序”。好吧,该应用程序有规格。当生成输入表单时,db 也将定义数据库。 “例如,名为“姓名”的列可能变成“工资”是没有意义的。这似乎不太可能;我的例子更现实,但是,我当然不能阻止用户做愚蠢的事情。那就是为什么列名不能是主键。但我认为有人决定重命名表单上的输入字段是不合理的。+1 花时间回复,谢谢 @mawg - 您缺少关于任何数据库驱动应用程序中最关键部分的规范:数据本身。它真正解开的地方是报道。从本质上讲,您需要先询问架构,然后才能构建驱动报告的数据。存储只是难题的一小部分。会扼杀项目的是性能和维护。这是一篇关于一个人使用 EAV 的经验的精彩文章,并注意他使用的很好的类比。 simple-talk.com/opinion/opinion-pieces/bad-carma @mawg - 如果预计对架构的更改不频繁,为什么不能提前收集规范?使用标准列构建规范化数据库会更好、更便宜。事实上,定制构建三个不同的数据库比构建一个 EAV 来统治它们来存储所有三个系统的信息要便宜。 + 用于询问,但是“如果预计对架构的更改不频繁,为什么不能提前收集规范?”因为我被命令构建一个可以出售给数百甚至数千名客户的表单生成器,并使他们能够设计自己的输入表单,是的,我确实问过我们是否可以为他们创建表单和数据库,并被告知没有。 @mawg - 听起来管理层想要我在之前文章中提供的文章链接中提到的相同类型的应用程序。这将是一个很好的案例研究,让他们了解为什么这样的系统站不住脚,更重要的是,无利可图。【参考方案3】:就像托马斯说的,理性的数据库不擅长解决你的问题。但是,您可能想看看像 MongoDB 这样的 NoSQL 数据库。
【讨论】:
【参考方案4】:请看这篇文章: http://www.simple-talk.com/opinion/opinion-pieces/bad-carma/ 其他人对您的问题的体验。
【讨论】:
【参考方案5】:这是针对 A) 和 B) 的,不是我做过的事情,但我认为这是 Reddit 使用的一个有趣的想法,请参阅此链接(查看 第 3 课):
http://highscalability.com/blog/2010/5/17/7-lessons-learned-while-building-reddit-to-270-million-page.html
【讨论】:
这对于低重要性社交书签类型的应用程序可能非常有用,但对于工资单应用程序来说,这将是一场彻底的灾难。 谢谢,+1 因为我似乎没有很好地解释我的问题,所以我会看看它是否有用。 您还注意到,任何关于 Reddit 数据库的报告或分析的提及都被可疑地省略了。【参考方案6】:不确定数据库,但对于图表而不是使用 PHP 作为图表,我建议使用 javascript (http://www.reynoldsftw.com/2009/02/6-jquery-chart-plugins-reviewed/)。这样做的好处是一些处理被卸载到客户端以进行图表显示,并且它们可以是交互式的。
【讨论】:
谢谢。我通常对客户端的东西保持警惕,因为它可能会被关闭、被黑客入侵等,但我会调查一下。【参考方案7】:其他受访者是正确的,您应该对这种方法非常谨慎,因为它比传统的关系模型更复杂且性能更低 - 但我已经做了这种类型的事情来适应工作中的部门差异,并且它奏效了它的使用量很好。
基本上我是这样设置的,首先 - 一个表来存储有关用户想要创建的表单的一些信息(显然,根据需要进行调整):
--************************************************************************
-- Create the User_forms table
--************************************************************************
create table User_forms
(
form_id integer identity,
name varchar(200),
status varchar(1),
author varchar(50),
last_modifiedby varchar(50),
create_date datetime,
modified_date datetime
)
然后是一个表格,用于定义要在表单上显示的字段,包括任何限制 以及它们要呈现的顺序和页面(我的应用程序将这些字段呈现为 多页向导类型的流程)。
-
-************************************************************************
-- Create the field configuration table to hold the entry field configuration
--************************************************************************
create table field_configuration
(
field_id integer identity,
form_id SMALLINT,
status varchar(1),
fieldgroup varchar(20),
fieldpage integer,
fieldseq integer,
fieldname varchar(40),
fieldwidth integer,
description varchar(50),
minlength integer,
maxlength integer,
maxval varchar(13),
minval varchar(13),
valid_varchars varchar(20),
empty_ok varchar(1),
all_caps varchar(1),
value_list varchar(200),
ddl_queryfile varchar(100),
allownewentry varchar(1),
query_params varchar(50),
value_default varchar(20)
);
然后我的 perl 代码将循环遍历字段以显示第 1 页并将它们放在“向导表单”中......“下一步”按钮将按顺序显示第 2 页字段等。
我也有 javascript 函数来强制执行为每个字段指定的限制...
然后是一个表格来保存用户输入的值:
--************************************************************************
-- Field to contain the values
--************************************************************************
create table form_field_values
(
session_Id integer identity,
form_id integer,
field_id integer,
value varchar(MAX)
);
这对于您想要做的事情来说是一个很好的起点,但请注意性能,因为如果添加 1000 个自定义字段,它确实会减慢任何报告的速度。 :-)
【讨论】:
+1 罗恩,这很可能会得到答案。我真的没有看到问题(可能是因为我是 n00b)并怀疑我只是解释得很糟糕。 这不是什么大问题——只是大多数“运行时可配置”类型的系统不适合许多开发人员的对象/数据模型心态。与任何类型的系统一样,需要权衡取舍。 :-) 并不是系统不适合开发人员的心态,而是模式不适合关系模型的主要设计。这样做的效果是,报告或查询数据,即从您的数据中获取信息,是一种负担并且表现不佳。现在,如果您永远不必针对数据进行报告,那就完全不同了。以上是关于如何设计一个布局可能随时间变化的通用数据库?的主要内容,如果未能解决你的问题,请参考以下文章