Reputation: 4537
I have the following 2 tables
table_article:
id subject tags
---------------------
1 subject-1 2,4,5
2 subject-2 3,5
3 subject-3 1,2
4 subject-4 2,3,4
5 subject-5 3
table_tags:
id tag_name
---------------------
1 php
2 jQuery
3 css
4 mysql
5 java
and I'm trying to get results like
id => 1, subject => subject-1, tag_names => jQuery,mysql,java
id => 2, subject => subject-2, tag_names => css,java
id => 3, subject => subject-3, tag_names => php,jQuery
Here is my current attempt, which returns ONLY the first tag (e.g. 2 instead of 2,4,5 for row 1)
1 SELECT
2 table_article.id,
3 table_article.subject,
4 GROUP_CONCAT(table_tags.tag_name) AS tag_names
5 FROM
6 table_article
7 LEFT JOIN
8 table_tags
9 ON
10 (table_tags.tag_id IN (table_article.tags))
11 GROUP BY
12 table_article.id
13 LIMIT
14 3
and the results are
id => 1, subject => subject-1, tag_names => jquery
id => 2, subject => subject-2, tag_names => css
id => 3, subject => subject-3, tag_names => php
The problem occurs on line 10 -> IN (table_article.tags)
.
I just can't figure out how could I solve this problem, can anyone help please?
Upvotes: 0
Views: 166
Reputation: 5740
Well, I would use IN
in this situation, it won't work, replace it with FIND_IN_SET(table_tags.tag_id, table_article.tags) > 0
and you'll be fine. Though you really should normalize this.
Upvotes: 0
Reputation: 7347
As other said, this is not a good design.
Instead, you could change your table design this way:
table_article
id
subject
article_tag
article_id
tag_id
table_tags
id
tag_name
Life would be much easier this way :-)
Upvotes: 0
Reputation: 562861
You can't use a string that happens to contain commas as a list of discrete values.
In other words this:
ON table_tags.tag_id IN (2,4,5)
Is not the same as this:
ON table_tags.tag_id IN ('2,4,5')
The numeric value of a string like '2,4,5' is the initial numeric portion, and the remainder after the first non-numeric character is ignored. So the string '2,4,5' has a numeric value of 2. It won't be an error, but it won't get you what you intended, which is a match against any of the values in the comma-separated list.
MySQL has a built-in function FIND_IN_SET()
which does understand strings that contain comma-separated values. The function returns the position of the matching value, or 0 if no match was found.
ON FIND_IN_SET(table_tags.tag_id, '2,4,5') > 0
But this cannot use an index and it forces you to run a table-scan which is going to kill your performance. To be clear, I don't recommend using this function in a join condition.
The answer is: Don't store tags in a comma-separated list. See my answer for Is storing a comma separated list in a database column really that bad?
Store one tag per row in a separate table, as @Martin Lyne suggests. That way you can look for the right tag with =
and you can even index the column for much better performance.
Upvotes: 3
Reputation: 3065
I've not seen an IN in a ON before (not saying it's not valid) but I would do ON table_tags.tag_id = table_article.tags)
So you end up with multiple rows
subject-1, query
subject-1, css
subject 2, query
then the GROUP BY
would compress the table and the GROUP_CONCAT
gets all the missing tags.
Upvotes: 1