如何设计一个布局可能随时间变化的通用数据库?

时间:2010-06-10 02:19:18

标签: php sql database database-design dynamic-sql

这是一个棘手的问题 - 我如何以编程方式创建和查询其内容我无法预见的数据库?

我正在实现一个通用的输入表单系统。用户可以使用WYSIWYG布局创建PHP表单,并将其用于他希望的任何目的。他也可以查询输入。

所以,我们有三个阶段:

  1. 设计并生成表单。虽然可以稍后编辑表单,但这是一次性过程。这设计了数据库。
  2. 某人或几个人使用表格 - 例如每日销售报告,库存,工资单等。他们对表格的输入被写入数据库。
  3. 其他人,也许是管理层,可以查询数据库并生成报告。
  4. 由于这些表格是通用的,我无法预测数据库结构 - 除了说它将反映HTML表单字段并且包含来自编辑框,备忘录,单选按钮等的集合的数据输入。

    问题和评论:

    A)如何在表和列方面最好地构建数据库?主键怎么样?我的第一个想法是使用控件名称来标识每一列,然后我意识到用户可以编辑表单并重命名,这样可能“名称”变为“员工”或“工资”变为“:薪水”。我倾向于每个人都有一个唯一的号码。

    B)如何最好地键入行?我在想一个允许我查询的时间戳和来自A)

    的行Id的列

    C)我必须处理列重命名/插入/删除。敌人删除,我不确定是否从数据库中删除数据。即使用户不再从表单输入它,他也可能希望查询先前输入的内容。或者可能存在一些保留数据的法律要求。列中的任何问题都重命名/插入/删除?

    D)对于查询,我可以让我的PHP查询数据库以获取列名并生成一个带有列表的表单,其中每个条目都有一个数据库列名,一个复选框说明它是否应该在查询中使用,基于列类型,一些选择标准。这应该足以构建像“position ='高级推销员'和薪水> 50k”这样的搜索。

    E)我可能需要生成一些花哨的图表 - 图表,直方图,饼图等,以便随着时间的推移查询数值数据的结果。我需要为此找到一些好的FOSS PHP。

    F)我还忘记了什么?

    对我来说这一切看起来都很棘手,但我是数据库n00b - 也许你对大师来说很简单?


    编辑:请不要告诉我不要这样做。我没有任何选择: - (

    编辑:在现实生活中,我不希望列重命名/插入/删除频繁。但是,运行几个月后可能需要更改数据库。我确信这种情况经常发生。我担心我已经严重地提出了这个问题并且人们认为每10分钟左右就会发生变化。

    实际上,我的用户将在布置表单时定义数据库。他们可能第一次就做对了,永远不会改变它 - 特别是如果他们从纸质表格转换。即使他们决定改变,这可能只会发生一次或两次,经过数月或数年 - 这可能发生在任何数据库中。

    我认为我在这里没有特殊情况,也不认为我们应该专注于改变。或许更好地专注于联系 - 什么是一个好的主键计划?或许,对于一个文本输入,一个数字和一个备忘录?

8 个答案:

答案 0 :(得分:9)

  

“这对我来说似乎很棘手,但是   我是数据库n00b - 也许是   你对大师来说很简单吗?“

不,它确实 很棘手。从根本上说,您所描述的不是数据库应用程序,而是数据库应用程序 builder 。事实上,听起来好像你想编写类似Google App Engine或MS Access的网页版本的代码。编写这样的工具将需要很多的时间和专业知识。

Google通过使用其BigTable平台实施了灵活的架构。它允许您随意弯曲架构。问题是,这种灵活性使得很难编写诸如“position ='高级销售员'和薪水> 50k”之类的查询。

所以我认为NoSQL方法不是你需要的。您希望构建一个生成和维护RDBMS模式的应用程序。这意味着您需要设计一个元数据存储库,您可以从中生成动态SQL以构建和更改用户的模式,并生成前端。

您的元数据架构需要存储的东西

对于模式生成:

  • 外键关系(EMPLOYEE在DEPARTMENT中工作)
  • 唯一的业务键(只能有一个名为“Sales”的DEPARTMENT)
  • 参考数据(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'
/

疯狂就是这样。

答案 1 :(得分:7)

实质上,您正在询问如何构建没有规范的应用程序。关系数据库的设计并非如此,以便您可以有效地执行此操作。这个问题的常见方法是实体 - 属性 - 值设计,对于您想要使用它的系统类型,失败的几率几乎是100%。

例如,名为“Name”的列可能成为“Salary”是没有意义的。如果工资值可能有“Fred”,“Bob”,100K,1000,“很多”,那么报告在哪里可以获得总工资?数据库的设计并不是为了让任何人放置任何东西。成功的数据库模式需要结构,这意味着需要在需要存储的内容和原因的规范方面付出努力。

因此,为了回答你的问题,我会重新考虑这个问题。尝试制作可以在宇宙中存储任何内容的应用程序的整个方法并不是成功的秘诀。

答案 2 :(得分:3)

像托马斯说的那样,理性数据库并不擅长你的问题。但是,您可能需要查看像MongoDB这样的NoSQL dbs。

答案 3 :(得分:2)

看到这篇文章: http://www.simple-talk.com/opinion/opinion-pieces/bad-carma/ 为了别人对你的问题的体验。

答案 4 :(得分:1)

这适用于A)& B),并不是我做过的事情,但认为Reddit投入使用是一个有趣的想法,请看这个链接(查看第3课):

http://highscalability.com/blog/2010/5/17/7-lessons-learned-while-building-reddit-to-270-million-page.html

答案 5 :(得分:1)

不确定数据库,但对于图表而不是使用PHP作为图表,我建议使用javascript(http://www.reynoldsftw.com/2009/02/6-jquery-chart-plugins-reviewed/)。对此有利的是,一些处理被卸载到客户端以进行图表显示,并且它们可以是交互式的。

答案 6 :(得分:1)

其他受访者认为你应该对这种方法非常谨慎是正确的,因为它比传统的关系模型更复杂,性能更差 - 但是我已经做了这种事情以适应部门在工作中的差异,并且它起作用了它的使用量很好。

基本上我是这样设置的,首先是一个表来存储一些用户想要创建的表格的信息(显然,根据需要进行调整):

--************************************************************************
-- 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个自定义字段,它可以真正减慢任何报告的速度。 : - )

答案 7 :(得分:1)

我同意马克的观点,利用他人的经验可以防止许多不可预见的错误 “数据库n00b”,请参阅专注于通用后端的this article工作的人company