【问题标题】:What is the preferred way to store custom fields in a SQL database?在 SQL 数据库中存储自定义字段的首选方式是什么?
【发布时间】:2010-01-19 08:29:50
【问题描述】:

我的朋友正在开发一种产品,供不同的独立医疗单位使用。

数据库存储在不同时间进行的大量测量值,例如温度、血压等......

让我们假设它们保存在一个名为exams 的表中,其中包含temperaturepressure 等列(以及idpatient_idtimestamp)。大多数测量值都存储为浮点数,但有些是其他类型(字符串、整数...)

虽然其中许多测量是由他们的产品处理的,但它需要允许不同的医疗单位记录和处理其他自定义测量。一个非常漂亮的 UI 允许管理员编辑这些自定义字段,指定它们的名称、类型、可能的值范围等......

他不确定如何存储这些自定义字段。

他倾向于一张单独的表(比如一张表custom_exam_data,其中包含exam_idcustom_field_idfloat_valuestring_value、...等字段)

我担心这会使搜索更难实现,效率也更低。

我倾向于直接修改考试表(同时避免列名与某些方案发生冲突,例如在所有自定义字段前加上下划线或将它们命名为 custom_1,...)

他担心动态修改数据库并为每个医疗单位使用不同的模式。

希望有更多经验的人可以在这个问题上发表意见。

注意事项:

  • 他正在使用 Ruby on Rails,但我认为这个问题几乎与框架无关,除了他只在 SQL 数据库中寻找解决方案这一事实。

  • 我稍微简化了问题,因为自定义字段需要可用于多个表,但我相信这不会真正影响采取的方向。

  • (添加)一个非常通用的报告模块将需要搜索、排序、生成这些数据的统计信息等,因此需要将这些数据存储在适当类型的列中

  • (已添加)对于标准字段和自定义字段,将过滤用户输入。例如,将检查给定范围内的数字(不能有 -12 或 +444 的温度)等...因此,转换为适当的 SQL 类型不是问题。

【问题讨论】:

    标签: sql database database-design


    【解决方案1】:

    这些年来我不得不多次处理这种情况,我同意你最初的想法,即直接修改数据库表,并使用动态 SQL 生成语句。

    创建字符串 UserAttribute 或 Key/Value 列一开始听起来很吸引人,但它会导致 inner-platform effect 最终不得不重新实现外键、数据类型、约束、事务、验证、排序、分组、计算,等人。在您的 RDBMS 中。您也可以只使用平面文件而不是 SQL。

    SQL Server 提供INFORMATION_SCHEMA 表,让您可以在运行时创建、查询和修改表架构。它已经内置了完整的类型检查、约束、事务、计算以及您需要的一切,不要重新发明它。

    【讨论】:

    • 感谢您指出该术语(内部平台效应)。这对我来说是新的,但我觉得它在这里非常适用。
    【解决方案2】:

    奇怪的是,当有一个有据可查的模式时,有这么多人为此提出临时解决方案:

    Entity-Attribute-Value (EAV) Model

    两种选择是 XML 和嵌套集。 XML 更易于管理,但通常速度较慢。嵌套集通常需要某种类型的专有数据库扩展才能做到,而不会造成混乱,例如 SQL Server 2005+ 中的 CLR 类型。它们违反了第一范式,但仍然是性能最快的解决方案。

    【讨论】:

    • +1 for XML:灵活到第 n 级,并且可以通过使用 Typed-XML 列在 SQL Server 上提高性能(尽管输入列会限制灵活性——但如果灵活的 XSD 架构是使用有可能实现公平平衡)
    • 感谢 EAV 的链接。这不适用于这里;例如,如果医疗单位决定为他们的目的添加血液中的糖含量很重要,那么他们将使用自定义字段添加这一点,这将为绝大多数客户输入。如果我要序列化任何东西,我会使用 YAML,但序列化在这里是一个糟糕的主意(请参阅最后 2 个注释)
    • 我所知道的任何数据库都不支持 YAML(并且没有模式规范),而 XML 可以在其中的几个数据库中进行强类型化和索引。我还提供了嵌套集合的可能性,它会更高效。我不认为您有相互冲突的要求这一事实值得投反对票。
    • +1 用于 XML。我相信 SQL Server 2005 XML 字段也可以被索引。我已经看到了一个专有框架(基于 sql2005 构建),它完全按照问题中的要求执行,在数十万行中执行,就像它们在普通关系数据中一样。它还可以添加向下钻取搜索功能,使用可感知数据的 UI 组件(即带有查找值的组合框、向下钻取 xpath 的条件等)。
    • 我的反对意见并没有任何冒犯的意思。我确实相信 EAV 模式不适用于这个问题,尽管我可能表现得很糟糕?我收回我对 YAML 的评论,实际上这在这里毫无意义。
    【解决方案3】:

    Microsoft Dynamics CRM 通过在每次进行更改时更改数据库设计来实现这一点。我觉得很恶心。

    我会说一个更好的选择是考虑一个属性表。尽管这些通常不受欢迎,但它为您提供了所需的灵活性,并且您始终可以使用动态 SQL 创建视图以再次将数据转出。只需确保在创建这些视图时始终使用 LEFT JOIN 和 FK,这样查询优化器才能更好地完成工作。

    【讨论】:

      【解决方案4】:

      我在商业会计软件包中看到了您朋友的想法的使用。该表被分成两个,第一个包含由系统单独定义的字段,第二个包含诸如 USER_STRING1、USER_STRING2、USER_FLOAT1 等字段。这些表通过标识值链接(当一条记录插入主表时,一条具有相同标识的记录插入第二个)。每个需要用户字段的表都是这样拆分的。

      【讨论】:

        【解决方案5】:

        好吧,每当我需要在数据库字段中存储一些未知类型时,我通常将其存储为String,根据需要对其进行序列化,同时存储数据的类型。

        这样,您可以拥有任何类型的数据,使用任何类型的数据库。

        【讨论】:

        • 我今天早上刚刚因为推荐连载而被撕毁了一个新的。很高兴看到其他人看到了他们的目的。
        • @Chuck Vose,将内容存储为字符串的最佳方式。
        • 不错的建议,但排序或其他计算效果不佳。我将此添加到我的问题中。
        【解决方案6】:

        我倾向于将测量值作为字符串 (varchar) 存储在数据库中,另一列标识测量类型。我的理由是,它可能会以字符串形式来自 UI,并且转换为任何其他数据类型可能会在存储用户输入之前引入损坏。

        不利的一面是,当您通过某些度量指标过滤结果集时,您仍然需要执行强制转换,但至少存储和持久性机制不会引入损坏。

        【讨论】:

        • 不错的建议,但排序或其他计算效果不佳。无论如何,过滤用户输入很容易(而且是强制性的!),以确保它们在可接受的范围内(如果是数字)。无论如何,该过滤已经为标准字段编写了。我将此添加到我的问题中。
        【解决方案7】:

        我不能告诉你最好的方法,但我可以告诉你 Drupal 如何在仍然使用当今可用的标准 RDBMS 的同时实现一种无模式结构。

        一般的想法是有一个包含字段列表的模式表。每行实际上只有两列,'table':String 列和 'column':String 列。对于这些列中的每一个,它实际上定义了一个只有一个 id 和该列的实际数据的整个表。

        真正的诀窍在于,当您处理数据时,与列出所有可能列的捆绑表的连接永远不会超过一个连接,因此您最终不会像您想象的那样损失速度。与您提议的 custom_ 前缀不同,这也将使您能够比仅仅少数几家医疗公司扩展得更远。

        MySQL 在返回具有少量列的短行的行数据方面非常快。通过这种方式,这个方案很快就结束了,同时为您提供了很大的灵活性。

        至于搜索,我的建议是索引页面内容而不是数据库内容。使用 Solr 解析呈现的页面并保存指向实际页面的链接,而不是尝试使用巧妙的 SQL 搜索数据库。

        【讨论】:

        • 如果我理解正确,Drupal 混合了这两种策略?它动态修改数据库的模式(通过添加表)并且必须进行连接(因为数据被拆分到不同的表中)......我看不到好处,这似乎是两种方法中最糟糕的,但也许我是缺少什么?
        • 哈,你可能真的不是。每个表只有一列有一些非常好的性能提升。无论如何,它完全奇怪的结构,我不会用它来交换我的 redis 密钥库。
        • 我有点喜欢这种方法,每个键的特定表,将具有正确的数据类型,并且可以具有由数据库引擎强制执行的约束,您可以对大多数搜索数据进行精确索引。
        【解决方案8】:

        定义两个新表:custom_exam_schemacustom_exam_data

        custom_exam_data 有一个 exam_id 列,另外还有一个用于每个自定义属性的列。

        custom_exam_schema 将有一个 row 来描述如何解释 custom_exam_data 表的每个 。它会有nametypeminValuemaxValue 等列。

        因此,例如,要创建一个自定义字段来跟踪一个人的手指数量,您可以将('fingerCount', 'number', 0, 10) 添加到custom_exam_schema,然后将一个名为fingerCount 的列添加到exam 表中。

        有人可能会说在运行时更改数据库架构不好,但我认为配置这些自定义字段是设置的一部分,不会经常发生。不过,这种方法让您可以随时处理更改,并且不会冒弄乱您的核心表架构的风险。

        【讨论】:

        • 谢谢。我会将这视为对我建议的解决方案的投票。
        【解决方案9】:

        假设您朋友的数据库必须存储来自多个来源的数据值,例如人口统计学值、诊断、干预、生理学值、生理检查值、住院值等。

        他可能还需要定义选择,假设他的数据库缺少种族,而单位工作人员需要患者的种族(不同种族更不可能患上某些疾病),他们可能想要使用下拉菜单有几种选择。

        我建议使用具有这些选项的其他表,或者您是否只使用“Custom_field_choices”表,该表在某些时候完全相同但名称不同。

        考虑到数据库:
        - 需要灵活
        - 可以添加和自定义来自多个表的数据
        - 您可能希望保持数据库主要结构的完整性以实现分布和统一性
        - 数据必须有限制以及警报和警告
        - 数据必须有单位(10 公斤或 10 磅)?
        - 该数据可以有多种选择
        - 数据可以具有不同的权限(从简单用户到管理员)
        - 这些数据可能需要在不修改代码的情况下生成报告(自动化)
        - 在不修改代码的情况下,可能需要这些数据在系统内进行交叉引用分析

        自定义表格是我的解决方案,修改每个表格最终风险太大。

        【讨论】:

        • 我想知道存在什么样的实际风险。
        【解决方案10】:

        我会将这些自定义字段存储在一个表中,其中每条记录(dataType、dataValue、dataUnit)将在一行中使用。所以从一个样本到数据会有一个关系 oneToMany 。您还可以创建一个表格来记录您将使用的所有类型的cutsom 类型。例如:

        create table DataType
        (
        id int primary key,
        name varchar(100) not null unique
        description text,
        uri varchar(255) //<-- can be used for an ONTOLOGY
        )
        
        
        create table DataRecord
        (
        id int primary key,
        sample_id int not null,//<-- reference to the sample
        dataType_id int not null, //<-- references DataType
        value varchar(100),//<-- the value as string
        unit varchar(50)//<-- g, mg/ml, etc... but it could also be a link to a table describing the units just like DataType
        )
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-06
          • 1970-01-01
          • 2023-03-09
          相关资源
          最近更新 更多