2

我最近在考虑一些事情,我想知道做类似以下场景的正确方法是什么(我相信对于 DB 人来说做类似的事情是很常见的事情)。

假设您有一个产品表,如下所示(MySQL):

CREATE TABLE `products` (
  `id` int(11) NOT NULL auto_increment,
  `product_name` varchar(255) default NULL,
  `product_description` text,
  KEY `id` (`id`),
  KEY `product_name` (`product_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

这里没有什么不寻常的。现在假设在不同的表中有一个类别的层次结构,并且有一个单独的表将多对多关系与 products 表绑定 - 这样每个产品都属于某种类别(我将省略那些,因为这不是这里的问题)。

现在到了有趣的部分——如果每个类别都要求产品项目附加一组变量。例如,计算机显示器类别中的产品必须具有 LCD/CRT 枚举字段、屏幕尺寸枚举等 - 以及其他一些类别,假设冰淇淋有一些其他变量,如风味 varchar、货架存储时间 int 等。

这里的问题在于所有产品都有一组共同的变量(id、name、description 和类似的),但是还有其他变量在类别之间不一致 - 但所有产品都应该共享共同的集合,因为最后它们都属于产品组,因此可以查询例如 SELECT * FROM products ORDER BY company_id (琐碎的例子,可能不具有代表性,但你明白了)。

现在,我看到了几个潜在的解决方案:
- 为每个产品类别生成单独的表,并在其中存储带有适当附加变量的产品 - 愚蠢且查询不友好

- 产品表与公共变量保持相同,并为每个类别创建一个单独的表,其中包含额外的将两个表与 JOIN 绑定的变量 - 标准化,但查询性能和清晰度问题 - 如何从类别中过滤产品(第一个表 - 产品)和额外的变量过滤器(即 17 英寸 LCD 显示器) - 它需要 SQL JOIN 技巧

- 产品表保持不变并添加另一个变量类型文本,例如包含其他变量的 JSON 数据 - 紧凑且整洁,但无法使用 SQL 过滤变量

我知道我在这里遗漏了一些非常明显和简单的东西——我对标准化技术有点生疏了:)


编辑:在没有成功提出这个问题之前,我一直在搜索 stackoverflow。然而,在我发布问题后,我点击了我的一个标签“规范化”,发现了几个类似的问题,导致查找“泛化专业化关系设计”。故事的重点是,这一定是我的互联网生活中第一次出现标签在搜索中真正有用的情况。但是,我仍然想听听你们的意见和意见。
编辑2:方法 2 的问题是我希望大约 1000 个专业化。类别有一个层次结构(1-4 级深),端节点添加了专门的变量 - 它们以大约 1000 个的顺序累积,因此添加专门的表来连接有点不切实际。
编辑3:由于在我的案例中存在大量属性波动,建议的“实体属性值”看起来像是要走的路。查询噩梦来了!多谢你们。

4

3 回答 3

4

您期望有多少种产品类型?他们每个人都有自己的应用程序逻辑吗?

您可以创建一个称为“实体属性值”模型的通用模型,但是当您尝试处理产品的特定属性时,它有很多陷阱。简单的搜索查询有时会变成真正的噩梦。基本思想是您有一个包含产品 ID、属性名称(或属性表中的 ID)和值的表。您还可以添加表格以保存每种产品类型的模板。因此,一组表会告诉您任何给定产品它可以具有的属性(可能还有有效的值范围),而另一组表会告诉您任何单个产品的值是什么。

不过,我会强烈警告不要使用此模型,因为在您必须实际实施之前,这似乎是一个非常巧妙的想法。

如果您的产品类型数量有限,我会选择您的第二种解决方案 - 一个具有基本属性的主产品表,然后是每种特定产品类型的附加表。

于 2009-03-19T16:06:20.070 回答
2

我一直在做这个Oracle

我有以下表格:

t_class (id RAW(16), parent RAW(16)) -- holds class hierachy.
t_property (class RAW(16), property VARCHAR) -- holds class members.
t_declaration (id RAW(16), class RAW(16)) -- hold GUIDs and types of all class instances
t_instance (id RAW(16), class RAW(16), property VARCHAR2(100), textvalue VARCHAR2(200), intvalue INT, doublevalue DOUBLE, datevalue DATE) -- holds 'common' properties

t_class1 (id RAW(16), amount DOUBLE, source RAW(16), destination RAW(16)) -- holds 'fast' properties for class1.
t_class2 (id RAW(16), comment VARCHAR2(200)) -- holds 'fast' properties for class2
--- etc.

RAW(16)是在哪里Oracle举行GUID

如果要选择对象的所有属性,请发出:

SELECT  i.*
FROM    (
        SELECT  id 
        FROM    t_class
        START WITH
                id = (SELECT class FROM t_declaration WHERE id = :object_id)
        CONNECT BY
                parent = PRIOR id
        ) c
JOIN    property p
ON      p.class = c.id
LEFT JOIN
        t_instance i
ON      i.id = :object_id
        AND i.class = p.class
        AND i.property = p.property

t_property保存您通常不会搜索的内容(例如,文本描述等)

快速属性实际上是数据库中的普通表,以提高查询效率。它们仅保存某个类或其后代的实例的值。这是为了避免额外的连接。

您不必使用快速表并将所有数据限制在这四个表中。

对于您的任务,它看起来像这样(为简洁起见,我将使用方括号中的字符串而不是 GUID):

t_class

标识父母

[ClassItem] [ClassUnknown]
[ClassMonitor] [ClassItem]
[ClassLCD] [ClassMonitor]

t_property

类属性

[ClassItem] 价格
[ClassItem] 供应商
[ClassItem] 模型
[ClassMonitor] 大小
[ClassLCD] 矩阵类型

t_declaration

身份证类
[1] [ClassLCD] -- 饭山 ProLite E1700

t_instance -- 我会将所有值放在一列中,不考虑类型(INT、VARCHAR 等)

id 类属性值

[1] [ClassItem] 价格 $300
[1] [ClassItem] 供应商饭山
[1] [ClassItem] 型号 ProLite E1700s
[1] [ClassMonitor] 尺寸 17
[1] [ClassLCD] matrixType TFT

如果您需要一些复杂的查询来搜索,比如sizeAND matrixType,您可以将它们从propertyinstance创建另一个表中删除:

t_lcd (id RAW(16), 大小 INT, matrixType VARCHAR2(200))

id 大小 matrixType

[1] 17 TFT

并使用它与其他属性连接,而不是t_declaration在上面的查询中。

但是即使没有快速表,这种模型也是可行的。

于 2009-03-19T15:17:29.327 回答
0

这种模式有一个名称。它被称为“泛化专业化”。

如果你搜索“泛化专业化建模”,你会得到一些关于如何做到这一点的文章。其中一些文章倾向于关系建模和 SQL,而另一些则倾向于对象建模。

于 2009-03-20T06:54:39.590 回答