如何选择所有具有特定标签的帖子?

时间:2017-06-08 20:22:23

标签: mysql sql join

这是我的表结构:

// posts
+----+-----------+---------------------+-------------+
| id |   title   |        body         |   keywords  |
+----+-----------+---------------------+-------------+
| 1  | title1    | Something here      | php,oop     |
| 2  | title2    | Something else      | html,css,js |
+----+-----------+---------------------+-------------+

// tags
+----+----------+
| id |   name   |
+----+----------+
| 1  | php      |
| 2  | oop      |
| 3  | html     |
| 4  | css      |
| 5  | js       |
+----+----------+

// pivot
+---------+--------+
| post_id | tag_id |
+---------+--------+
| 1       | 1      |
| 1       | 2      |
| 2       | 3      |
| 2       | 4      |
| 2       | 5      |
+---------+--------+

好吧,我有两个标签(phphtml),我需要选择所有用它们标记的帖子。我怎么能这样做?

目前我使用REGEXP,只需选择我想要的内容:

SELECT * FROM posts WHERE keywords REGEXP 'php|html';

请参阅?我甚至不使用1 join。这些天我的数据集长大了,我的查询需要一段时间才能执行。我想我必须使用像join这样的关系功能。但是我不确定它会比我当前的查询更好。

无论如何,有谁知道,我怎样才能更快地得到预期的结果?

2 个答案:

答案 0 :(得分:1)

正则表达式的处理速度很慢。使用LIKE可能会提供更好的响应时间:

SELECT * 
FROM   posts 
WHERE  (keywords LIKE '%php%' OR keywords LIKE '%html%')

基于规范化表的查询将是:

SELECT     posts.id, posts.title, posts.body, posts.keywords 
FROM       posts
INNER JOIN pivot ON pivot.post_id = posts.id
INNER JOIN tags ON tags.id = pivot.tag_id
WHERE      tags.name IN ('html', 'php')
GROUP BY   posts.id

为获得最佳速度,您必须确保id字段被声明为主键,并且您有索引:

tags(name)
pivot(tag_id)

但是,如果所有帖子中的很大一部分符合条件,这将不会比您当前的解决方案更快:它可能会更慢。但是,如果例如不到1%的帖子满足条件,那么这可能会表现得更好,因为原则上执行计划不需要包括对整个帖子表的扫描。

答案 1 :(得分:1)

您已经拥有一个具有多对多关系的规范化设计。在posts表中没有必要使用keywords列,因为pivot已经建立了相同的列。

您只需要正确进行连接即可。试试这个:

SELECT posts.id
  FROM posts
LEFT OUTER JOIN pivot
  ON posts.id = pivot.post_id
LEFT OUTER JOIN tags
  ON pivot.tag_id = tags.id
WHERE tags.name = "php" or tags.name = "html"
GROUP BY posts.id;

这将为您提供带标签的帖子的所有ID。