3

我有以下表格。

文章
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)

4

2 回答 2

7

Create a new table:

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

(this is the SQL server syntax, may be slightly different for MySQL)

This is called a "Junction Table" or a "Mapping Table" and it is how you express Many-to-Many relationships in SQL. So, whenever you want to add a Category to an Article, just INSERT a row into this table with the IDs of the Article and the Category.

For instance, you can initialize it like this:

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

Now you can remove c_id from your Articles table.

To get back all of the Categories for a single Article, you would do use a query like this:

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}

Alternatively, to return all articles that have a category LIKE a certain string:

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}+'%'
             )

(You may have to adjust the last line, as I am not sure how string parameters are passed MySQL+PHP.)

于 2013-08-30T20:22:40.070 回答
1

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

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

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

在具有许多行的大型 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

如果您有一个包含许多记录的大型 Articles 表,这两个查询总是会触发对表 Articles 和两个 DEPENDENT SUBQUERYS 的完整表扫描。这意味着即使您只需要该类别中的文章,性能也取决于文章行数。

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 方式也更有效 注意我已经养成了一个习惯,即我从记录数最少的表开始,如果你从表开始,我会按照我的方式工作。文章执行将是如果 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 的性能

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

存在方式

http://sqlfiddle.com/#!2/48817/21 / http://sqlfiddle.com/#!2/c11f3/1(更多文章)解释告诉我两种模式或多或少相同,但 'php ' 应该快一点,因为在 TYPE 列中关闭 const 类型与 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 以获取可扩展的示例在有很多记录的大表上,这是主题启动者需要的

于 2013-09-01T20:07:43.990 回答