在关系型数据库中存储“元数据”的正确方法是什么?

4

我有一个名为assets的表格,一个资产可以属于用户、团队或部门,并可能多次属于每个角色。我的问题是这些资产高度可变,它们可以具有与每个资产不同的属性。

例如: 这些可以是资产:

1.)
type:workbench
cost:200
vendor:Acme Co.
color:black
2.)
type:microscope
serial_no:BH-00102
purchase_date:1337800923
cost:2040

这可能涉及到数百到数千种不同类型的资产。

如果我想以规范化的方式存储这种数据,以便轻松查询,而不必每次添加新的资产类型就更改我的表格,该怎么办呢?某些字段也适用于所有资产,例如成本。

到目前为止,我认为应该有:

assets
id,cost,purchase_date,asset_type_id

asset_types
id,name

division_assets
division_id,asset_id

user_assets
user_id,asset_id

但我不知道应该把变化的数据放在哪里。


一些属性适用于所有资产,但其他不适用的属性将特定于各个资产类型。 - Ethan
1
你打算在这些变量属性上进行搜索吗?另外,阅读EAV的相关内容。 - Remus Rusanu
1
如果您正在使用Postgres,您可以使用hstore模块来非常高效地完成此操作。 - user330315
@a_horse_with_no_name 谢谢分享,我之前不知道这个!https://www.postgresql.org/docs/current/hstore.html - Tiago Martins Peres
1
@TiagoMartinsPeres:在2022年,最好使用jsonb - user330315
显示剩余2条评论
5个回答

4

过去我曾经面临过这种情况,“最佳”答案总是取决于我想在数据库中处理多少,还是在客户端代码中处理多少。

就我个人的经验而言,在过去实践中效果最好的方法通常是每个可选属性对应一张表(特别是,并非每个实体类型对应一张表)。因此,在你上面的例子中,

assets (as per your example)
asset_types (as per you example)
division_assets (as per your example)
user_assets (as per your example)
colours
  asset_id, colour
weights
  asset_id, weight
serial_numbers
  asset_id, serial_number

当然,这取决于您需要做出的权衡,这对您可能不是一个好选择。就我个人而言,我喜欢尽可能明确地保留数据模式,包括数据类型和限制条件,所以当新属性出现时,我会毫不犹豫地更改表格。


3
我建议这样做:
assets (

   id
   asset_type_id
   vendor_id
   cost
   purchase_date

)

asset_poperties (

    id
    asset_id
    asset_property_type_id
    value

)

asset_property_types (

     id
     property_type

)

asset_types (

   id
   asset_type

)

vendors (

   id
   vendor

)

这不允许使用可变字段。 - Ethan
因此,那些会发生变化的属性存储在资产表中,而那些通用的属性则存储在单独的表格中(如资产类型、供应商等),并通过外键与资产表相连。 - Martin Petransky
但是用户可以添加不同的资产类型,这些类型将附带不同的数据,但我不希望用户能够更改数据库的结构。 - Ethan
你可以创建一个类似于asset_properties的附加表,包含id、asset_property_type、value等字段,然后将其与asset表关联。 - Martin Petransky
我考虑过这个,但会产生非常尴尬的查询。 - Ethan
不只是一个JOIN,没有别的了。实际上,在这种情况下,最好单独查询附加属性列表。 - Martin Petransky

2
您可以为资产元数据添加另一个表格。
asset_metadata
asset_metadata_id,asset_id,metadata_name,metadata_value

如果你想规范和分类元数据,可以按照以下方式对其进行规范化:
asset_metadata
asset_metadata_id,asset_id,metadata_name_id,metadata_value

metadata_name
metadata_name_id,metadata_name_text

2

我建议将像费用这样的常见属性放在传统列中。然后再添加一个列,在其中放置所有其他可变资产属性的序列化集合。

CREATE TABLE assets (
  asset_id INT AUTO_INCREMENT PRIMARY KEY,
  cost NUMERIC(9,2),
  purchase_date DATE,
  variables TEXT
);

您可以将集合序列化为JSON或XML或任何您想要的格式。使用最容易被应用程序代码处理的格式。

INSERT INTO assets VALUES (123, 49.95, CURDATE(), 'color: black; vendor: Acme Co.');

优点是您可以随时向文本块添加新属性。缺点是您无法读取或写入单个属性,必须将整个集合视为一个整体。
但是,您可以索引单个属性以使其可搜索。您需要为要进行搜索的每个属性创建一个新表(但这可能只是全部属性的一小部分)。
CREATE TABLE asset_color (
  asset_id INT NOT NULL,
  color VARCHAR(10),
  PRIMARY KEY (asset_id, color),
  KEY(color)
);

并不是所有的资产都记录在这个表格中,只有那些具有颜色的资产才会被记录。

然后,您可以对所有具有颜色属性的资产进行索引搜索:

SELECT assets.*
FROM assets INNER JOIN asset_color USING (asset_id);

您还可以进行索引搜索,仅限于具有颜色属性且颜色为黑色的资产:
SELECT assets.*
FROM assets INNER JOIN asset_color USING (asset_id)
WHERE color = 'black';

设计规范化数据库时,没有办法允许可变属性的存在。所有的标准形式都要求表格首先是一个关系(relation)。而按照定义,关系必须具备一组固定的属性。

其他人建议使用EAV表格,但是EAV中的“value”列不符合带有类型的关系列的定义(此外,EAV表格中约束条件也无法起作用)。因此,EAV表格不是一个关系,也不能满足任何标准形式。


1
将序列化属性放入单个列中也不是规范化的,因为它违反了第一范式(原子值)。使用规范化形式的关系型数据库解决这个问题也是不可能的。 - user330315

-1

您可以创建两个新表:

1)在以下表中定义多个资产属性(尽可能多地包含资产属性)

asset_id

asset_attribute

asset_value

2)asset_attribute表

attribute_id

asset_attribute

逻辑是需要首先在asset_attribute表中定义asset_attributes,然后可以将其与任何资产链接/标记(作为外键,在UI上的下拉列表中),并输入适当的值。

希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接