MySQL Many-to-Many Relationship Select Conditions
I have two tables books and tags which are in a many-to-many relationship via the table books_tags. What I want to do is select all books that have one or more tag(s) like '%tag%'. No problem. However, I also have a group_concat field which I want to be filled with all the tags that are related to the selected book.
My select query basically looks like this:
select
flbdb_books.id,
flbdb_books.title,
flbdb_tags.id,
flbdb_tags.name,
group_concat(distinct concat('["', flbdb_tags.id, '","', flbdb开发者_StackOverflow社区_tags.name, '"]') separator ',') as tags
from
flbdb_books
join flbdb_books_tags
on flbdb_books.id = flbdb_books_tags.book_id
join flbdb_tags
on flbdb_tags.id = flbdb_books_tags.tag_id
group by flbdb_books.id;
This delivers the desired result if I don't have any conditions. I use PHP and json_decode to read the tags field. I have tried three different ways to filter the results, but none of them works.
use where:
where flbdb_tags.name like '%poetr%'
however, this means that the tags field (the one that is assembled using group_concat) is only filled with this tag
use having
having flbdb_tags.name like '%poetr%'
this does not return all books tagged poetry, because in some cases the poetry tag is hidden behind another tag due to the group by
use having on the group_concat field
having tags like '%poetr%'
this would actually do the trick, but I need to do the case-insensitive searching, and upper(tags) doesn't work
I hope it is clear what I want to do.
Thanks
There is a relational query that does exactly what you need.
The insight is that the filter tag is your starting point, and you actually need to join from the tag to the book back on to tags.
select
books.id,
books.title,
tags.id,
tags.name,
group_concat(distinct concat('["', tags.id, '","', tags.name, '"]') separator ',') as tags
from flbdb_tags filter_tag
join flbdb_books_tags filter_book_tags
on filter_tag.id = filter_book_tags.tag_id
join flbdb_books books
on filter_book_tags.book_id = books.id
join flbdb_books_tags books_tags
on books.id = books_tags.book_id
join flbdb_tags tags
on tags.id = books_tags.tag_id
where filter_tag.name like '%poetr%'
group by books.id;
You need to join twice, not once since the tag you filter is a different set of data then all tags for a book.
Will it work for you?
select
flbdb_books.id,
flbdb_books.title,
COUNT(CASE
WHEN flbdb_tags.name LIKE '%poetr%' THEN 1
END) as num_poetry,
group_concat(distinct concat('["', flbdb_tags.id, '","', flbdb_tags.name,
'"]') separator ',') as tags
from
flbdb_books
join flbdb_books_tags
on flbdb_books.id = flbdb_books_tags.book_id
join flbdb_tags
on flbdb_tags.id = flbdb_books_tags.tag_id
group by flbdb_books.id
HAVING num_poetry >0;
Not sure I clearly understand, but I'd try to wrap your original select and do the filtering in the wrapper select
select * from (
select
flbdb_books.id,
flbdb_books.title,
flbdb_tags.id,
flbdb_tags.name,
group_concat(distinct concat('["', flbdb_tags.id, '","', flbdb_tags.name, '"]') separator ',') as tags
from
flbdb_books
join flbdb_books_tags
on flbdb_books.id = flbdb_books_tags.book_id
join flbdb_tags
on flbdb_tags.id = flbdb_books_tags.tag_id
group by flbdb_books.id) inner_select
where upper(inner_select.name) like '%herpderp%'
精彩评论