做到在一个完全规范化的方式,你需要这样的事:
的MEANING_ITEM有以下指标:
- {SITE_ID,MEANING_NO,ITEM_NO} - 为主键自动创建并支持使用给定标签高效搜索项目。
- {ITEM_NO,SITE_ID,MEANING_NO} - 可以高效地查询相反的内容:“获取给定项目的标签”。
注意:如果您的DBMS支持它,请考虑clustering此表。集群表中的二级索引可能很昂贵(因为它们需要包含整个PK的副本并可能导致双重查找),但在这种情况下,两个索引都包含相同的字段(因此所有“额外”字段都已存在于次级索引),并且没有索引之外的字段,因此不需要进行双向查找。通过聚类,你只需要消除(无用的)表堆,而只剩下两个B树。
这种模式具有以下特性:
- 两个标签和物品的部位特异性地识别和您查询默认站点特定的标签。如果您想查询标签名称而不考虑网站,只需在下面的查询中从WHERE子句中省略
SITE_ID = ...
即可。由于TAG_NAME处于TAG PK的领先优势,因此无需额外索引即可有效地满足无站点查询。
- 项目不能标记来自“错误”网站的标签。我们使用标识关系,这些关系沿着“菱形”依赖关系的两个边缘向下传播SITE_ID,并合并到“菱形”(在MEANING_ITEM中)的底部,这就是我们保证的原因。
- 标记同义词有效地表示(在同一站点内具有相同含义的标记被视为同义词)。如果我们试图在标签上实现M:N自我关系,那么就没有可能发生的各种异常情况。
- 由于标签的含义是特定于站点的,所以同义词也是站点特定的。
- 意义表是存储有关标记(如描述)的附加信息的自然地方,它将由所有同义词共享。
我们将如何处理同义词传递性?如果A,B和C是同义词,我们只是存储A-B和B-C,或者我们也存储A-C?我们如何执行它?如果我们不强制执行它,我们需要某种递归查询来选择所有依赖关系。我们需要为每个连接添加一行,浪费空间和性能。
与要获得项目特定标记的任何,你需要执行一个类似的查询......
SELECT *
FROM ITEM
WHERE EXISTS (
SELECT *
FROM TAG JOIN MEANING_ITEM ON
TAG.SITE_ID = MEANING_ITEM.SITE_ID
AND TAG.MEANING_NO = MEANING_ITEM.MEANING_NO
WHERE
TAG.SITE_ID = <site id>
AND TAG.NAME IN (<list of tags>)
AND ITEM.SITE_ID = MEANING_ITEM.SITE_ID
AND ITEM.ITEM_NO = MEANING_ITEM.ITEM_NO
)
注:我们完全可以忽略从JOIN到意义上面的查询 - JOIN所需的所有字段已经在TAG中。
对于有所有特定标记的项目,你需要一些计算,与此类似:
SELECT *
FROM ITEM
WHERE <number of tags> = (
SELECT COUNT(DISTINCT TAG_NAME)
FROM TAG JOIN MEANING_ITEM ON
TAG.SITE_ID = MEANING_ITEM.SITE_ID
AND TAG.MEANING_NO = MEANING_ITEM.MEANING_NO
WHERE
TAG.SITE_ID = <site id>
AND TAG.NAME IN (<list of tags>)
AND ITEM.SITE_ID = MEANING_ITEM.SITE_ID
AND ITEM.ITEM_NO = MEANING_ITEM.ITEM_NO
)
现在这个貌似很多JOIN-ING的,但这种模式是优秀的对于聚簇(又名index-organized)表和covering带索引的查询。
您可能需要先考虑实际StackExchange的数据量,然后再出于性能原因考虑对该设计进行非规范化(例如,删除联结表并限制每个项目的标记数)。
在任何情况下,在进行任何特定设计之前都要测量实际的数据量。
标签如何管理?在大多数站点(如StackOverflow)中,用户可以快速方便地创建标签。标签是否与正式类别相同,即严格在编辑过程中进行管理,还是他们是特设的,人群智慧型的东西? – 2012-07-12 17:11:06
与Stack类似,他们只是键入单词。虽然我们可能需要一个最低水平来创建新的 – 2012-07-13 08:55:43