开发者

MySQL Many-to-Many Relationship Select Conditions

开发者 https://www.devze.com 2023-04-02 01:56 出处:网络
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. Howev

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.

  1. 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

  2. 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

  3. 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%'
0

精彩评论

暂无评论...
验证码 换一张
取 消