简体   繁体   中英

How can I select all posts which have specific tags?

Here is my table structure:

// 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      |
+---------+--------+

Ok well, I have two tags ( php and html ) and I need to select all posts tagged with them. How can I do that?

Currently I use REGEXP and simply select what I want like this:

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

See? I don't use even 1 join . These days my dataset is grown up and my query takes a while to be executed. I guess I have to use a relational feature like join . However I'm not sure it would be better than my current query.

Anyway, does anybody know, how can I get the expected result faster?

Regular expressions can be slow to process. Using LIKE will probably give better response times:

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

The query based on the normalised tables would be:

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

For optimal speed you must ensure that the id fields are declared as primary keys, and that you have indexes on:

tags(name)
pivot(tag_id)

Still, this will not be faster than your current solution if a significant part of all the posts fulfil the condition: it could well be slower. But if for example less than 1% of the posts would satisfy the condition, then this will likely perform better, as in principle the execution plan does not need to include a scan of the whole posts table.

You already have a normalized design with a many to many relationship. There is no need to have the keywords column in the posts table, as the pivot already establishes the same.

You just need to do the join properly. Try this:

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;

This will give you all the id's of the posts with the tags.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM