oracle 关系数据库中自定义字段的设计模式

cwdobuhd  于 2023-10-16  发布在  Oracle
关注(0)|答案(4)|浏览(126)

我已经分配了一个任务来创建(相对)简单的报告系统。在这些系统中,用户将看到一个表格的结果报告。一个表有一些字段,每个字段给予用户每条记录中的部分信息。然而,我的问题是,每个报告字段将不会由开发人员声明。必须由系统的用户声明。所以我的报告表是动态的。
我在' Data Driven Custom View Engine in ASP.NET MVC '中看到了使用Asp.net MVC框架创建动态表单的示例,但我不知道这是否适合我的系统。

更新1:

目前,我以以下实体关系图结束:

在上图中,我将报告的每条记录存储在Report表中。我还将报告类型存储在ReportType中。对于报告记录中使用的每个字段,我将使用ReportFieldValue。字段类型将存储在ReportField中。
所以如果我想添加一个记录到我的数据库,我首先添加一行到Report表。然后,对于每个添加的记录字段,我将在ReportFieldValue表中添加一行。
然而,正如你可能注意到的,在这些方法中,我必须将每个字段值存储在char(255)中。问题在于datetime这样的字段类型不应该存储为字符串。对于这种类型的系统,是否有任何设计模式或体系结构?

elcex8rz

elcex8rz1#

避免使用字符串类型的数据,将VALUE替换为NUMBER_VALUEDATE_VALUESTRING_VALUE。这三种类型在大多数情况下都很好。如果需要的话,可以在以后添加XMLTYPE和其他花哨的列。对于Oracle,使用VARCHAR2而不是CHAR来节省空间。
始终尝试将值存储为正确的类型。原生数据类型更快(因为类型转换是不必要的,而且优化器可以更好地推理值并生成更智能的执行计划),更小,更容易使用,更安全。
Oracle有一个通用的数据类型系统(ANYTYPE、ANYDATA和ANYDATASET),但这些类型很难使用,在大多数情况下应该避免使用。
架构师通常认为对所有数据使用单个字段会使事情变得更容易。它使得生成数据模型的漂亮图片变得更容易,但它使其他事情变得更加困难。考虑这些问题:
1.如果不知道数据的类型,你就不能对数据做任何有趣的事情。即使是显示数据,知道文本的类型也很有用。在99.9%的用例中,用户可以很明显地看出3列中的哪一列是相关的。
1.针对字符串类型的数据开发类型安全的查询是很痛苦的。例如,假设您要查找在本千禧年出生的人的“出生日期”:

select *
 from ReportFieldValue
 join ReportField
     on ReportFieldValue.ReportFieldid = ReportField.id
 where ReportField.name = 'Date of Birth'
     and to_date(value, 'YYYY-MM-DD') > date '2000-01-01'

你能找到窃听器吗?上面的查询是危险的,即使你以正确的格式存储了日期,很少有开发人员知道如何正确地修复它。Oracle进行了优化,使得很难强制执行特定的操作顺序。为了安全起见,你需要这样一个查询:

select *
    from
    (
        select ReportFieldValue.*, ReportField.*
            --ROWNUM ensures type safe by preventing view merging and predicate pushing.
            ,rownum
        from ReportFieldValue
        join ReportField
            on ReportFieldValue.ReportFieldid = ReportField.id
        where ReportField.name = 'Date of Birth'
    )
    where to_date(value, 'YYYY-MM-DD') > date '2000-01-01';
You don't want to have to tell every developer to write their queries that way.
j5fpnvbx

j5fpnvbx2#

您的设计是实体属性值(Entity Attribute Value,EAV)数据模型的变体,EAV数据模型通常被视为数据库设计中的反模式。
也许更好的方法是创建一个包含300列(从NUMBER_VALUE_1到NUMBER_VALUE_100、VARCHAR2_VALUE_1..100和DATE_VALUE_1..100)的报告值表。
然后,围绕跟踪哪些报表使用哪些列以及它们使用每列的目的来设计数据模型的其余部分。
这有两个好处:首先,您没有将日期和数字存储在字符串中(其优点已经指出),其次,您避免了与EAV模型相关的许多性能和数据完整性问题。

EDIT --添加EAV模型的一些经验结果

使用Oracle 11 g2数据库,我将30,000条记录从一个表转移到EAV数据模型中。然后,我查询模型以获取这30,000条记录。

SELECT SUM (header_id * LENGTH (ordered_item) * (SYSDATE - schedule_ship_date))
FROM   (SELECT rf.report_type_id,
               rv.report_header_id,
               rv.report_record_id,
               MAX (DECODE (rf.report_field_name, 'HEADER_ID', rv.number_value, NULL)) header_id,
               MAX (DECODE (rf.report_field_name, 'LINE_ID', rv.number_value, NULL)) line_id,
               MAX (DECODE (rf.report_field_name, 'ORDERED_ITEM', rv.char_value, NULL)) ordered_item,
               MAX (DECODE (rf.report_field_name, 'SCHEDULE_SHIP_DATE', rv.date_value, NULL)) schedule_ship_date
        FROM   eav_report_record_values rv INNER JOIN eav_report_fields rf ON rf.report_field_id = rv.report_field_id
        WHERE  rv.report_header_id = 20 
        GROUP BY rf.report_type_id, rv.report_header_id, rv.report_record_id)

结果如下:
这是22秒得到30,000行,每行4列。这是 * 方式 * 太长了。从一张平的table上看,我们会在2秒内看到,很容易。

aemubtdh

aemubtdh3#

使用MariaDB,它是Dynamic Columns。实际上,这使您可以将所有的杂项列放到一个列中,但仍然给予对它们的有效访问。
我会在它们自己的列中保留一些公共字段。
More discussion of EAV和建议(以及如何在没有动态列的情况下实现)。

u1ehiz5o

u1ehiz5o4#

好吧,你有一个关于以正确的数据类型存储数据的非常好的观点。
我同意这确实给用户定义的数据系统带来了问题。
解决这个问题的一种方法是为每个数据类型组(整数,浮点数,字符串,二进制和日期)添加表,而不是将值保存在ReportFieldValue表中。然而,这将使您的工作变得更加困难,因为您必须选择并连接多个表才能获得一个结果。
另一种方法是在ReportFieldValue中添加一个数据类型列,并创建一个用户定义的函数来动态地将数据从字符串转换为适当的数据类型(使用数据类型列中的值),以便您可以使用它进行排序,搜索等。
Sql server也有一种名为sql_variant的数据类型,它应该支持多种类型,尽管我从未使用过它,但它的文档似乎很有前途。

相关问题