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

Oba*_*aid 28 database database-design relational-database

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

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

我应该怎么解决这个问题?

Dam*_*vic 37

这称为观察模式.

在此输入图像描述

三个对象,例如

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'
Run Code Online (Sandbox Code Playgroud)

这是表格的样子:

Entity
EntityID    Name            Description
   1        'Book'            'To read'
   2        'Cat'             'Fury cat' 
   3        'Beer Bottle'     'To ship beer in'
Run Code Online (Sandbox Code Playgroud)

.

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'
Run Code Online (Sandbox Code Playgroud)

.

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
Run Code Online (Sandbox Code Playgroud)

.

Measurement
PropertyID     Unit       Value 
    6          'inch'       9          -- cat, tail length
    7          'ml'        500         -- beer bottle, volume
Run Code Online (Sandbox Code Playgroud)

.

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
Run Code Online (Sandbox Code Playgroud)

编辑:

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
;
Run Code Online (Sandbox Code Playgroud)

从评论中使用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 ;
Run Code Online (Sandbox Code Playgroud)

这看起来很复杂,但仔细观察后,您可能会注意到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'
;
Run Code Online (Sandbox Code Playgroud)

  • 这是一个EAV.根据Bill Karwin的SQL反模式http://www.amazon.co.uk/SQL-Antipatterns-Programming-Pragmatic-Programmers/dp/1934356557/ref=ntt_at_ep_dpt_1/278-1154409-5580237 (6认同)
  • 这是一个非常糟糕的主意,它将导致进一步的问题.请不要这样做. (4认同)
  • 我仍然不喜欢这种方法,但你的回答对这个讨论是一个很好的贡献.我把我的投票改为投票. (4认同)
  • 你真的是指"愤怒的猫",还是"毛茸茸的猫".好吧,也许你有一只像我前女友那样的猫,可以称之为愤怒的猫. (2认同)

Jef*_*dge 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.
Run Code Online (Sandbox Code Playgroud)

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

  • 事实上,我也喜欢超级类型-子类型——问题是当子类型表的数量达到数千时会发生什么?如何处理动态添加新类型的情况?在这种情况下会推荐什么?本机 XML 存储或... (3认同)
  • @bukzor ,@Damir Sudarevic - 事实上,我应该回答这个问题,我很遗憾没有这样做。答案是数据库的设计应该反映已知的真实世界情况。如果要添加新的“类型”,那么只有在这些类型中一致的事物才能进行关系建模。可能需要某种属性/值系统,但仅限于那些“软”的东西。基本上,如果系统用户而不是程序员可以修改某些内容,那么它必须存储为数据而不是结构。 (3认同)
  • 我有一个类似的问题,我正在创建一个仓库管理系统,不同的产品有不同的属性。根据您所描述的结构,允许网站的管理员用户添加新的产品类型是否可以接受? (2认同)
  • 这是个好主意...除非您有 100 个属性。这就是您消除关系数据库概念并进行非规范化的地方。标准做法。即使是最学术的数据库书籍也会告诉您非规范化有一个时间和地点。这是其中之一。 (2认同)

mie*_*war 5

您可以使用无模式方法:

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

这种技术的优点:

  1. 更少的查询:您可以在一个查询中获得所有信息,不需要“定向”查询(获取元元数据)和连接。

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

  3. 由于它是 JSON,因此您不需要在后端进行额外的处理。您的网页(我假设它是一个 Web 应用程序)只是从您的 Web 服务读取 JSON,就是这样,您可以随心所欲地将 JSON 对象与 javascript 一起使用。

问题:

  1. 潜在的空间浪费,如果您有 100 本书的作者相同,那么所有书只有 author_id 的作者表更节省空间。

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

根据规模,这可能是一种矫枉过正。在较小的规模加入会工作得很好。