数据库设计启用多个标签,如Stackoverflow?

时间:2013-08-30 20:13:29

标签: mysql database database-design database-schema junction-table

我有以下表格。

  

文章
   a_id INT主要唯一
  名称 VARCHAR
  说明 VARCHAR
   c_id INT

     

类别
   id INT
   cat_name VARCHAR

现在我只使用

  

SELECT a_id,name,Description,cat_name FROM Articles LEFT JOIN Category ON Articles.a_id=Category.id WHERE c_id={$id}

这给了我属于某个类别的所有文章以及类别名称。
每篇文章只有一个类别

我以类似的方式使用子类别(我有另一个名为sub_cat的表)。
但是每篇文章都没有必要的子类别。它可能属于多个类别。

我现在想到用多个类别标记一篇文章,就像stackoverflow上的问题被标记一样(例如:有多个标签,如PHP,MYSQL,SQL等)。

以后我必须显示(过滤)所有文章与某些标签(例如:标记为php,php + MySQL),我还必须显示标签以及文章名称,描述。
任何人都可以帮我重新设计数据库吗?(我在后端使用php + MySQL)

2 个答案:

答案 0 :(得分:7)

创建一个新表:

CREATE TABLE ArticleCategories(
    A_ID INT,
    C_ID INT,
    Constraint PK_ArticleCategories Primary Key (Article_ID, Category_ID)
)

(这是SQL服务器语法,对于MySQL可能略有不同)

这称为“连接表”或“映射表”,它是您在SQL中表达多对多关系的方式。因此,无论何时您想要在文章中添加类别,只需INSERT在该表中添加一行,其中包含文章和类别的ID。

例如,您可以像这样初始化它:

INSERT Into ArticleCategories(A_ID,C_ID)
    SELECT A_ID,C_ID From Articles

现在您可以从文章表中删除c_id

要获取单个文章的所有类别,您可以使用如下查询:

SELECT a_id,name,Description,cat_name 
FROM Articles 
LEFT JOIN  ArticleCategories ON Articles.a_id=ArticleCategories.a_id 
INNER JOIN Category ON ArticleCategories.c_id=Category.id 
WHERE Articles.a_id={$a_id}

或者,要返回具有 LIKE 类别的所有文章的某个字符串:

SELECT a_id,name,Description
FROM Articles 
WHERE EXISTS(   Select * 
                From ArticleCategories 
                INNER JOIN Category ON ArticleCategories.c_id=Category.id 
                WHERE Articles.a_id=ArticleCategories.a_id 
                  AND Category.cat_name LIKE '%'+{$match}+'%'
             )

(您可能需要调整最后一行,因为我不确定如何通过MySQL + PHP传递字符串参数。)

答案 1 :(得分:1)

好的RBarryYoung你问我一个参考/分析你得到一个

此参考/分析基于MySQL服务器的文档/源代码分析

INSERT Into ArticleCategories(A_ID,C_ID)
    SELECT A_ID,C_ID From Articles

在一个包含许多行的大型文章表中,此副本会将一个核心从CPU推到100%加载,并将创建一个基于磁盘的临时表,这会降低整个MySQL的性能,因为磁盘会因该副本而出现压力。 如果这是一次性过程,那么这并不是那么糟糕,但如果你每次都这样做的话,那就算一算..

SELECT a_id,name,Description
FROM Articles 
WHERE EXISTS(   Select * 
                From ArticleCategories 
                INNER JOIN Category ON ArticleCategories.c_id=Category.id 
                WHERE Articles.a_id=ArticleCategories.a_id 
                  AND Category.cat_name LIKE '%'+{$match}+'%'
             )

注意不要把sqlfriddle上的执行时间用于真正的繁忙服务器,时间变化很多,以便做出好的陈述,但看看View Execution Plan必须说什么

请参阅http://sqlfiddle.com/#!2/48817/21了解演示

如果您有一个包含许多记录的大型文章表,则两个查询始终会触发表格文章和两个相关子查询的完整表格扫描。 这意味着即使您只想要类别中的文章,性能也取决于文章行的数量。

Select * 
                From ArticleCategories 
                INNER JOIN Category ON ArticleCategories.c_id=Category.id 
                WHERE Articles.a_id=ArticleCategories.a_id 
                  AND Category.cat_name LIKE '%'+{$match}+'%'

此查询是内部子查询,但是当您尝试运行它时,MySQL无法运行,因为它依赖于Articles表的值,因此这是相关子查询。子查询类型,将为外部查询处理的每一行计算一次。确实不太好

还有更多方法可以重写RBarryYoung查询我将展示一个。 即使使用LIKE运算符,INNER JOIN方式也更有效率 注意我已经做了一个habbit,我开始使用记录数量最少的表,如果你从表开始工作我的方式如果MySQL优化器选择正确的计划,执行将是相同的。

SELECT 
   Articles.a_id
 , Articles.name
 , Articles.description
FROM 
 Category

INNER JOIN
 ArticleCategories
ON
 Category.id = ArticleCategories.c_id

INNER JOIN
 Articles
ON 
 ArticleCategories.a_id = Articles.a_id

WHERE 
 cat_name LIKE '%php%';
;

请参阅http://sqlfiddle.com/#!2/43451/23了解演示请注意,这看起来更糟糕,因为看起来需要检查更多行

请注意,如果Article表具有较低的数字关闭记录,RBarryYoung EXIST方式和INNER JOIN方式将根据执行次数执行或多或少相同,并且当记录计数变大时,INNER JOIN方式会更好地扩展< /强>

http://sqlfiddle.com/#!2/c11f3/1 EXISTS oeps更多文章记录现在需要检查(即使它们没有与ArticleCategories表链接)所以现在查询效率较低 http://sqlfiddle.com/#!2/7aa74/8 INNER JOIN与第一个演示相同的解释计划

关于扩展它的额外注意事项变得更加糟糕当你还想要ORDER BY或GROUP BY NOT EXIST方式时它会创建一个基于磁盘的临时表,这会破坏MySQL的性能

让我们分析LIKE'%php%'vs ='php'的EXIST方式和INNER JOIN方式

EXIST方式

http://sqlfiddle.com/#!2/48817/21 / http://sqlfiddle.com/#!2/c11f3/1(更多文章)解释告诉我两个模式或多或少相同,但'php'应该快一点,因为在TYPE列中关闭const类型vs ref但是LIKE%php%将使用更多的CPU,因为字符串比较算法需要运行。

INNER JOIN方式

http://sqlfiddle.com/#!2/43451/23 / http://sqlfiddle.com/#!2/7aa74/8(更多文章)解释告诉我LIKE'%php%'应该更慢,因为需要分析3行但是在这种情况下不会让人感到震惊(你可以看到索引并没有真正用于最佳方式。)

RBarryYoung方式有效,但至少保持性能至少不在MySQL服务器上 见http://sqlfiddle.com/#!2/b2bd9/1http://sqlfiddle.com/#!2/34ea7/1 对于将在具有大量记录的大型表上扩展的示例,这是主题启动者需要的