如何在列上存储元数据

时间:2009-06-26 20:15:05

标签: database-design normalization entity-attribute-value

假设您正在收集关于即将发布的超级英雄电影的内幕信息,您的主电影表看起来像这样:

表1

Title              Director   Leading Male      Leading Female    Villain
--------------------------------------------------------------------------
Green Lantern      Kubrick    Robert Redford     Miley Cyrus     Hugh Grant  
The Tick          Mel Gibson  Kevin Sorbo        Linda Hunt    Anthony Hopkins

这通常可以很好地工作,并且允许非常简单的查询以及行之间的比较。

但是,您希望跟踪每个数据事实的来源,以及发现事实的记者的姓名。这似乎表明某种EAV表是这样的:

表2

Movie             Attribute            Value          Source          Journalist
----------------------------------------------------------------------------------
Green Lantern      Director           Kubrick         CHUD              Sarah
Green Lantern    Leading Male      Robert Redford     CHUD              James
Green Lantern   Leading Female      Miley Cyrus    Dark Horizons        James
Green Lantern      Villain           Hugh Grant       CHUD              Sarah
The Tick           Director          Mel Gibson       Yahoo            Cameron
...

虽然它可以轻松捕获我们想要的元数据,但却更难以进行查询。简单地获取单个电影的所有基本数据需要更多。更具体地说,你必须在这里处理四行以获得绿灯侠的四个重要信息,而在表1中,它是一个封装良好的行。

所以我的问题是,鉴于我刚才描述的复杂情况,并且因为我知道EAV表一般要避免,EAV仍然是最好的解决方案吗?它似乎是表示此数据的唯一合理方式。我看到的唯一另一种选择是将表1与另一个包含这样的元数据的表格结合使用:

表3

Movie             Attribute            Source          Journalist
----------------------------------------------------------------------------------
Green Lantern      Director             CHUD              Sarah
Green Lantern    Leading Male           CHUD              James
Green Lantern   Leading Female      Dark Horizons         James
Green Lantern      Villain              CHUD              Sarah
The Tick           Director             Yahoo            Cameron
...

但这非常危险,因为如果有人将表1中的列名更改为“Villain”,那么表3中的行仍然会简单地说“Villain”,因此相关数据将被遗忘。如果“属性”列链接到另一个用作表1列的枚举的表,则可以帮助这一点。当然,DBA将负责维护此枚举表以匹配表1的实际列。实际上可以通过手动创建枚举表来进一步改进这一点,在SQL Server中使用系统视图,其中包含表1中列的名称。虽然我不确定您是否可以拥有涉及的关系系统视图。

你有什么建议? EAV是唯一的出路吗?

如果它只是一个元数据列(只有“来源”而没有“记者”),那么它仍然需要走EAV路线吗?您可以拥有“Director”,“Director_Source”,“Leading Male”,“Leading Male_Source”等列,但这很快就会变得难看。是否有一些我没想到的更好的解决方案?

如果我没有澄清任何一点,请发表评论,我会在必要时添加更多内容。哦是的,我使用的电影数据是伪造的:)

编辑:简要地重述我的主要问题,我希望得到表1的简单性和真正的RDBMS设计,它真正描述了一个电影条目,同时仍然将元数据存储在安全且易于访问的属性中方式。这可能吗?或者EAV是唯一的方式吗?

编辑2:在进行了一些网络研究之后,我还没有找到关于EAV的讨论,这些讨论的核心是希望在列上存储元数据。实现EAV的主要原因几乎总是动态且不可预测的列,在我的示例中并非如此。在我的例子中,总有相同的四列:导演,男主角,女主角,反派。但是,我想存储关于每一行的每列的某些事实(来源和记者)。 EAV会促进这一点,但我想避免诉诸于此。

更新

使用表2设计除了将“Movie”列重命名为“Name”并调用整个表“Movie”之外,这里是SQL Server 2008中的枢轴操作,以返回表1:

SELECT Name, [Director], [Leading Male], [Leading Female], [Villain]
FROM (Select Name, Attribute, Value FROM Movie) as src
PIVOT
(
Max(Value)
FOR Attribute IN ([Director], [Leading Male], [Leading Female], [Villain])
)  AS PivotTable

9 个答案:

答案 0 :(得分:6)

您可以在设计中更改您认为的事实值 ...您的数据模型中的事实似乎可以表示为以下N元组:

Movie | FactType | FactValue | FactSource | FactJournalist

下表结构应该支持您想要的数据模型,并且可以相对容易地编制索引和连接。您还可以创建一个视图,仅显示事实值和事实类型,以便您可以创建以下透视图:

MovieID | Movie Name | Director | LeadingMale | LeadingFemale | PrimaryVillain | etc

有趣的是,你可以认为这是将EAV模型完全应用于数据的逻辑扩展,并将单个电影(具有导演,导演,恶棍等的直观归属)分解为一个旋转结构,其中属性集中而是在信息来源上。

建议的数据模型的好处是:

  • 它被很好地规范化了(尽管你应该将FactType字段规范化为参考表以保证完整性)。
  • 可以创建一个视图,将事实类型有效地转换为表格结构
  • 它是相对可扩展的,允许数据库强制引用完整性和(如果需要)基数约束
  • MovieFact表可以被子类化以支持不同类型的电影事实,而不仅仅是那些简单的文本字段
  • 对数据的简单查询相对有效

数据模型的一些缺点是:

  • 复合条件查询更难(但并非不可能)编写(例如查找导演为A且领导男性为B等的所有电影......)
  • 该模型比较传统的方法或涉及EAV结构的方法稍微不那么明显
  • 插入和更新有点棘手,因为更新多个事实需要更新多行而不是多列

我将电影数据上升到一个级别来规范化结构,你可以将电影名称下移到MovieFact结构中以保持一致性(因为对于某些电影,我可以想象,即使这样,你可能想要追踪名字的来源信息)。

Table Movie
========================
MovieID   NUMBER, PrimaryKey
MovieName VARCHAR

Table MovieFact
========================
MovieID          NUMBER,  PrimaryKeyCol1
FactType         VARCHAR, PrimaryKeyCol2
FactValue        VARCHAR
FactSource       VARCHAR
FactJournalist   VARCHAR

您的虚构电影数据将如下所示:

Movie Table
====================================================================================
MovieID  MovieName
====================================================================================
1        Green Lantern
2        The Tick

MovieFact Table
====================================================================================
MovieID  FactType       FactValue         FactSource       FactJournalist
====================================================================================
1        Director       Kubrick           CHUD             Sarah
1        Leading Male   Robert Redford    CHUD             James
1        Leading Female Miley Cyrus       Dark Horizons    James
1        Villain        Hugh Grant        CHUD             Sarah
2        Director       Mel Gibson        Yahoo            Cameron
2        Leading Male   John Lambert      Yahoo            Erica
...

答案 1 :(得分:1)

有趣的场景。你可以把你的实体想象成一流的物体来绕过EAV贫民窟;让我们称他们为事实。在这种情况下,你有很好的正交性,因为每部电影都有完全相同的四个事实。您的EAV表可以是您原始/正确的表,然后您可以有一个外部进程来挖掘该表并将数据复制到正确规范化的表格(即您的第一个表)。通过这种方式,您可以获得所需的数据及其元数据,并且可以轻松查询电影信息,并准确了解挖掘过程的运行频率。

我认为你肯定需要一些“超出数据库”的力量来确保数据保持有效,因为似乎没有任何数据库内的方法来保持常规和EAV表的完整性。我想通过一系列复杂的触发器,你几乎可以完成任何事情,但是一个“解决”问题的人类管理员可能更容易处理。

答案 2 :(得分:1)

这是另一个想法......随意打孔:)

Table: Movie
Columns: MovieId|Movie|Director|LeadMale|LeadFemale|Villain

Table: MovieSource
Columns: MovieSourceId|MovieId|MovieRoleId|Source|Journalist

Table: MovieRole
Columns: MovieRoleId|MovieRole
Values: 1|Director, 2|LeadMale, 3|LeadFemale, 4|Villain

我在想的是电影表中的列可以属于不同的类型(在你的例子中,它们都是字符串/ varchars,但它们可以是数字或日期也有来源的信息。)

但是,源数据的列类型可能不会随电影数据的列类型而变化,因此您可以使用更多的EAV系统作为源,而不会丢失数据的完整性。

MovieRole表允许您显式枚举角色,以便您可以在源和影片表的给定单元格之间创建可靠的链接。

-Dan

答案 3 :(得分:1)

看到你只有源数据的两个字段(源和记者),我建议像这样的元数据表:

Movie    DirectorSource  DirectorJournalist  LeadingMaleSource  LeadingMaleJournalist ...
---------------------------------------------------------------------------------------
The Tick   Yahoo           Cameron           ...                ...

这会将不太重要的源数据保留在主表之外,但查询不会变得复杂,并且您的代码将更具可读性。

我只会建议EAV如果......

  • 您有超过3个源元数据字段
  • 需要能够轻松添加或更改电影字段。 (像'恶棍'到'小恶棍'的变化每天都要做几次)

答案 4 :(得分:0)

对于SO来说,我的回答似乎有点过于哲学。忍受我。

我认为“来源”列不是主题数据,而是元数据。这是关于我们如何了解其他一些数据的真实数据。这使得它成为关于数据和数据的数据。

EAV导致问题的原因之一是它将数据和元数据混合在一行中。有时我自己故意这样做,作为我想要实现的结果的中间步骤。但我已经尝试过不要在我的可交付成果中混合使用数据和元数据。

我知道为什么我从来没有这样做,但我无法简明扼要地解释。

答案 5 :(得分:0)

由于没有其他人真的对此表示反对,我将回答我自己的问题。我很确定一个类似EAV的桌子确实是唯一的出路。要在每个列上存储元数据(在这种情况下关于源和记者),您实际上将每个列本身视为一个实体,这就是EAV允许的。

可以去其他路线,比如为每个原始列添加第二和第三列来存储数据,但这肯定会破坏一些基本的规范化规则,并且可能只会让你以后痛苦。 / p>

答案 6 :(得分:0)

嗯......我没有用过这个,所以我不是根据经验说话(即如果它不起作用就不要责怪我),但从表面上看,似乎你可以存储“常见的” “您知道的数据将始终存在于普通表中,而”元数据“可能会更改为XML。接下来的问题是如何很好地查询它,我认为你可以像HERE所描述的那样做。

答案 7 :(得分:0)

另一种需要考虑的方法是类表继承。 Bill Karwin对this SO answer中的EAV选项进行了很好的评论,并且有很多良好的背景。

答案 8 :(得分:0)

我会根据编码需要做出决定。

如果src / journo只是附加信息,我会寻找更多专栏。但是,如果我知道我将最终构建复杂的src / journo查询,我会选择EAV,因为在元数据表中搜索记者的引用要比进入 LeadingFemaleJournalist 更容易em>和 VillainJournalist 等。

就个人而言 - 我倾向于将src / journo元数据转储到另一个表EAV风格,但使用FK来定义属性定义表。拥有自由格式的属性文本字段是灾难的处方 - 始终通过约束控制您的属性。如果需要,可以实现触发器以提高参照完整性。

对我而言,它归结为观点。您是否认为来源和记者本身就是关系问题,或者他们只是补充电影的其他数据?下一级别的改进将是为 MovieDataSource MovieDataJournalist 创建不同的表,这可以允许您将FK映射到定义有效 Sources 的表和记者(有关这些来源/记者的进一步信息可以充实)。您将在此处完成的是在 Movie 实体与 Source (以及 Journalist )实体之间建立多对多关系