如何在实体 - 属性 - 值设计中处理不同的数据类型(例如,每个数据类型有多列或多个表的单表)?

leb*_*olo 27 database oracle plsql database-schema entity-attribute-value

我想使用实体 - 属性 - 值(EAV)方法创建患者/样本元数据表.

:我应该如何处理的不同列类型的值(如字符串,数字,或外键的字典表)的基础上的属性

注意:我不是在问是否使用EAV方法.我查看了其他SO问题参考资料,并认为这是我用例的最佳方法(例如,我不想为每个属性创建一个单独的列或表- 可以数百个).但是,我会在一个全面的例子中重新考虑其他设计.

代表性数据

患者/样品(实体)可以有多个元数据属性(例如实验室位置,存活,肿瘤类型)每一个具有不同的类型(例如VARCHAR,NUMBER,FOREIGN_KEY*,分别地).

*FOREIGN_KEY表示该类型是一个外键ID( INTEGER)到的词典表的值(例如10种可能肿瘤类型的列表).因此,实验室位置可能VARCHAR因为我不关心这些值的标准化.但是肿瘤类型应该有一定程度的验证.

我的表格布局可能如下所示:

CREATE TABLE patients (
  patient_id INTEGER CONSTRAINT pk_patients PRIMARY KEY,
  patient_name VARCHAR2(50) NOT NULL
);

CREATE TABLE metadata_attributes (
  attribute_id INTEGER CONSTRAINT pk_metadata_attributes PRIMARY KEY,
  attribute_name VARCHAR2(50) NOT NULL,
  attribute_value_type VARCHAR(50) NOT NULL -- e.g. VARCHAR, NUMBER, or ID
);

CREATE TABLE patient_metadata (
  patient_id CONSTRAINT fk_pm_patients REFERENCES patients(patient_id) NOT NULL,
  attribute_id CONSTRAINT fk_pm_attributes REFERENCES metadata_attributes(attribute_id) NOT NULL,
  attribute_value ???
);
Run Code Online (Sandbox Code Playgroud)

我相信在metadata_attributes表中需要一个类型标识列(attribute_value_type)来知道要查找的列/表.

可能的方法

这是我能想到的两种可能的方法.

方法1:具有多列的单个EAV表

为patient_metadata表创建三个不同的列 - 每个对应一个类型.

CREATE TABLE patient_metadata (
  patient_id CONSTRAINT fk_pm_patients REFERENCES patients(patient_id) NOT NULL,
  attribute_id CONSTRAINT fk_pm_attributes REFERENCES metadata_attributes(attribute_id) NOT NULL,
  attribute_varchar_value VARCHAR(50),
  attribute_number_value NUMBER,
  attribute_id_value CONSTRAINT fk_pm_values REFERENCES some_table_of_values(value_id)
);
Run Code Online (Sandbox Code Playgroud)

方法2:多个EAV表

创建三个不同的patient_metadata表 - 每个类型一个.

CREATE TABLE patient_metadata_varchar (
  patient_id CONSTRAINT fk_pm_patients REFERENCES patients(patient_id) NOT NULL,
  attribute_id CONSTRAINT fk_pm_attributes REFERENCES metadata_attributes(attribute_id) NOT NULL,
  attribute_value VARCHAR(50) NOT NULL
);

CREATE TABLE patient_metadata_number (
  patient_id CONSTRAINT fk_pm_patients REFERENCES patients(patient_id) NOT NULL,
  attribute_id CONSTRAINT fk_pm_attributes REFERENCES metadata_attributes(attribute_id) NOT NULL,
  attribute_value NUMBER NOT NULL
);

CREATE TABLE patient_metadata_id (
  patient_id CONSTRAINT fk_pm_patients REFERENCES patients(patient_id) NOT NULL,
  attribute_id CONSTRAINT fk_pm_attributes REFERENCES metadata_attributes(attribute_id) NOT NULL,
  attribute_value CONSTRAINT fk_pm_values REFERENCES some_table_of_values(value_id) NOT NULL
);
Run Code Online (Sandbox Code Playgroud)

其他方法?

还有其他方法吗?

简而言之,我希望尽可能地尊重关系完整性,并允许数据库知道类型,以便它可以执行基本验证.但是,我相信上述两种方法都需要某种类型的手动完整性检查(方法1需要检查是否只填充了一个attribute_value列,等等).

该类型的我将执行将是典型的查询(例如检索的列表的值对于给定的元数据属性,检索的列表的值对于给定的患者(实体)和元数据属性等).我相信在大多数情况下我需要查询类型,以便知道要查询的列或表.还有其他方法吗?

所有方法(性能,查询结构等)的优缺点是什么?

第一次海报,所以提前感谢,请随时评论格式或进一步说明!

hak*_*aki 5

这是一个众所周知的问题。您提到的方法的问题在于,您需要在查询属性之前知道属性的类型。这不是世界末日,因为您管理元数据,但仍然......

两种可能的解决方案可能是

  1. 使用varchar2数据类型以已知格式表示所有数据类型。数字和字符没有问题,日期值可以以预定义的方式写入(就像to_String()在任何 OO 设计中实现一样)。
  2. 使用 ANYDATA 数据类型。我个人尝试过它,但决定不使用它。

  • -1 切勿将日期和数字存储为字符串。这会引起很多问题。例如,有人不可避免地会写这样的谓词:`where attribute = 'DOB' and to_date(value, 'YYYY-MM-DD') < date '2000-01-01'`。它看起来很合理,但非常危险,即使您的所有数据都是干净的。对不同类型使用不同的列不会导致额外的工作。您必须知道类型才能对数据执行任何有趣的操作。 (10认同)
  • 将数字存储为字符串显然不是最佳选择,但我不同意您的声明 - 这是一个设计问题,您在这里承担的“风险”取决于您的数据库访问层。 (4认同)
  • @lebolo 我最近使用 ANYDATA 构建了一个系统,但由于多种原因不得不替换 ANYDATA。1) 流水线函数存在严重的性能问题。2) API 太频繁地需要 PL/SQL,没有足够的“访问”功能。3) 类型不匹配不会引发异常,需要额外检查。4) 没有数据库工具支持 ANYDATA,包括 SQL*Plus、SQL Developer 或 PL/SQL 调试器。5) 未解决的错误,例如混合 32 位和 64 位客户端和服务器时出现的问题。 (4认同)