设计数据库以保存不同的元数据信息

时间:2010-07-13 20:06:13

标签: database-design database relational-database

所以我试图设计一个允许我将一个产品与多个类别连接起来的数据库。这部分我想通了。但我无法解决的问题是持有不同类型的产品细节。

例如,该产品可能是一本书(在这种情况下,我需要像isbn,作者等那样引用该书的元数据),或者它可能是商业列表(具有不同的元数据)。

我该如何解决这个问题?

6 个答案:

答案 0 :(得分:37)

这称为观察模式。

enter image description here

三个对象,例如

Book
Title = 'Gone with the Wind' 
Author = 'Margaret Mitchell'
ISBN   = '978-1416548898'

Cat
Name = 'Phoebe'
Color = 'Gray'
TailLength = 9 'inch'

Beer Bottle
Volume = 500 'ml'
Color = 'Green'

这就是表格的样子:

Entity
EntityID    Name            Description
   1        'Book'            'To read'
   2        'Cat'             'Fury cat' 
   3        'Beer Bottle'     'To ship beer in'

PropertyType
PropertyTypeID   Name        IsTrait         Description
   1            'Height'     'NO'       'For anything that has height' 
   2            'Width'      'NO'       'For anything that has width' 
   3            'Volume'     'NO'       'For things that can have volume'
   4            'Title'      'YES'      'Some stuff has title' 
   5            'Author'     'YES'      'Things can be authored' 
   6            'Color'      'YES'      'Color of things' 
   7            'ISBN'       'YES'      'Books would need this'
   8            'TailLength' 'NO'       'For stuff that has long tails'
   9            'Name'       'YES'      'Name of things'

Property
PropertyID   EntityID  PropertyTypeID      
    1           1              4     -- book, title
    2           1              5     -- book, author
    3           1              7     -- book, isbn
    4           2              9     -- cat, name
    5           2              6     -- cat, color
    6           2              8     -- cat, tail length
    7           3              3     -- beer bottle, volume
    8           3              6     -- beer bottle, color

Measurement
PropertyID     Unit       Value 
    6          'inch'       9          -- cat, tail length
    7          'ml'        500         -- beer bottle, volume

Trait
PropertyID         Value 
    1         'Gone with the Wind'     -- book, title
    2         'Margaret Mitchell'      -- book, author
    3         '978-1416548898'         -- book, isbn
    4         'Phoebe'                 -- cat, name
    5         'Gray'                   -- cat, color
    8         'Green'                  -- beer bottle, color

修改

Jefferey提出了一个有效的观点(见评论),所以我会扩大答案。

该模型允许动态(动态)创建任意数量的entites 没有架构更改的任何类型的属性。 Hovewer,这种灵活性具有价格 - 存储和搜索比通常的表格设计更慢,更复杂。

一个例子的时间,但首先,为了使事情变得更容易,我会将模型展平为视图。

create view vModel as 
select 
      e.EntityId
    , x.Name  as PropertyName
    , m.Value as MeasurementValue
    , m.Unit
    , t.Value as TraitValue
from Entity           as e
join Property         as p on p.EntityID       = p.EntityID
join PropertyType     as x on x.PropertyTypeId = p.PropertyTypeId
left join Measurement as m on m.PropertyId     = p.PropertyId
left join Trait       as t on t.PropertyId     = p.PropertyId
;

从评论中使用Jefferey的例子

with 
q_00 as ( -- all books
    select EntityID
    from vModel
    where PropertyName = 'object type'
      and TraitValue   = 'book' 
),
q_01 as ( -- all US books
    select EntityID
    from vModel as a
    join q_00   as b on b.EntityID = a.EntityID
    where PropertyName = 'publisher country'
      and TraitValue   = 'US' 
),
q_02 as ( -- all US books published in 2008
    select EntityID
    from vModel as a
    join q_01   as b on b.EntityID = a.EntityID
    where PropertyName     = 'year published'
      and MeasurementValue = 2008 
),
q_03 as ( -- all US books published in 2008 not discontinued
    select EntityID
    from vModel as a
    join q_02   as b on b.EntityID = a.EntityID
    where PropertyName = 'is discontinued'
      and TraitValue   = 'no' 
),
q_04 as ( -- all US books published in 2008 not discontinued that cost less than $50
    select EntityID
    from vModel as a
    join q_03   as b on b.EntityID = a.EntityID
    where PropertyName     = 'price'
      and MeasurementValue < 50 
      and MeasurementUnit  = 'USD'
)
select
      EntityID
    , max(case PropertyName when 'title' than TraitValue else null end) as Title
    , max(case PropertyName when 'ISBN'  than TraitValue else null end) as ISBN
from vModel as a
join q_04   as b on b.EntityID = a.EntityID
group by EntityID ;

这看起来很复杂,但仔细观察后,您可能会注意到CTE中的模式。

现在假设我们有一个标准的固定架构设计,其中每个对象属性都有自己的列。 查询看起来像:

select EntityID, Title, ISBN
from vModel
WHERE ObjectType       = 'book'
  and PublisherCountry = 'US'
  and YearPublished    = 2008
  and IsDiscontinued   = 'no'
  and Price            < 50
  and Currency         = 'USD'
;

答案 1 :(得分:14)

我不打算回答,但现在接受的答案有一个非常糟糕的主意。永远不应该使用关系数据库来存储简单的属性 - 值对。这将导致很多问题。

处理此问题的最佳方法是为每种类型创建一个单独的表。

Product
-------
ProductId
Description
Price
(other attributes common to all products)

Book
----
ProductId (foreign key to Product.ProductId)
ISBN
Author
(other attributes related to books)

Electronics
-----------
ProductId (foreign key to Product.ProductId)
BatteriesRequired
etc.

每个表格的每一行都应代表关于现实世界的命题,表格的结构及其约束应反映出所表现的现实。您越接近理想,数据就越清晰,报告和以其他方式扩展系统就越容易。它也会更有效地运行。

答案 2 :(得分:4)

您可以采用无模式方法:

将元数据保存在TEXT列中作为JSON对象(或其他序列化,但由于很快解释的原因,JSON更好)。

此技术的优点:

  1. 减少查询:您可以在一个查询中获取所有信息,无需“定向”查询(获取元元数据)和加入。

  2. 您可以随时添加/删除您想要的任何属性,无需更改表(这在某些数据库中存在问题,例如Mysql锁定表,并且需要很长时间才能使用大表)

  3. 由于它是JSON,因此您的后端无需额外处理。您的网页(我认为它是一个Web应用程序)只是从您的Web服务中读取JSON,就是这样,您可以使用javascript来使用javascript但是你喜欢。

  4. 问题:

    1. 潜在的浪费空间,如果您有100本同一作者的书籍,那么所有书籍只有author_id的作者表格在空间方面更经济。

    2. 需要实现索引。由于您的元数据是JSON对象,因此您不会立即拥有索引。但是,为您需要的特定元数据实现特定索引相当容易。例如,您希望按作者编制索引,因此您使用author_id和item_id创建author_idx表,当有人搜索作者时,您可以查找此表和项目本身。

    3. 根据比例,这可能是一种过度杀伤力。在较小规模的连接上工作得很好。

答案 3 :(得分:2)

应该输入产品。例如在产品表中包含type_id,它指向您将支持的产品类别,并让您知道要查询哪些其他表以获取相应的相关属性。

答案 4 :(得分:2)

在这类问题中,您有三种选择:

  1. 创建一个包含“generic”列的表。例如,如果您同时销售书籍和烤面包机,则您的烤面包机可能没有ISBN和标题,但它们仍然具有某种产品标识符和描述。因此,请为字段指定通用名称,例如“product_id”和“description”,对于书籍,product_id是ISBN,对于烤面包机,它是制造商的部件号等。
  2. 当真实世界的实体都以相同的方式处理时,这是有效的,至少在大多数情况下是这样,因此,如果不是“相同的”数据,则必须至少具有类似的数据。当存在真正的功能差异时,这会崩溃。就像对于烤面包机我们正在计算瓦特=伏特*安培,很可能没有相应的书籍计算。当你开始创建一个pages_volts字段,其中包含书籍的页数和烤面包机的电压时,事情已经失去控制。

    1. 使用Damir建议的属性/价值计划。请参阅我在帖子中对其优缺点的评论。

    2. 我通常建议的是类型/子类型方案。为“product”创建一个包含类型代码和通用字段的表。然后,对于每种真实类型 - 书籍,烤面包机,猫,等等 - 创建一个连接到产品表的单独表。然后,当您需要执行特定于图书的处理时,请处理书表。当您需要进行通用处理时,请处理产品表。

答案 5 :(得分:0)

我知道这可能不是您正在寻找的答案,但是很遗憾,关系数据库(SQL)是基于结构化预定义架构的思想而建立的。您正在尝试将非结构化无模式数据存储在不是为其构建的模型中。是的,您可以对其进行捏造,以便从技术上讲可以存储无限数量的元数据,但是这很快会引起很多问题并很快失去控制。只需查看Wordpress及其使用此方法所遇到的问题数量,您就可以轻松了解为什么它不是一个好主意。

幸运的是,这一直是关系数据库的一个长期存在的问题,这就是为什么开发使用文档方法的NoSQL无模式数据库并在过去十年中如此大幅度地普及的原因。这就是世界500强科技公司用来存储不断变化的用户数据的所有功能,因为它允许单个记录在保留在同一集合(表)中的同时,可以根据需要保留任意数量的字段(列)。

因此,我建议研究诸如MongoDB之类的NoSQL数据库,并尝试将其转换为它们,或与您的关系数据库一起使用。您知道需要用相同数量的列表示它们的任何类型的数据都应该存储在SQL中,而记录之间会有所不同的任何类型的数据都应该存储在NoSQL数据库中。