Самый эффективный способ проверить наличие нескольких отношений - PullRequest
1 голос
/ 20 июня 2019

Допустим, мне нужно найти все статьи, которые помечены всеми тремя тегами: food, lifestyle и health. Какой самый эффективный способ сделать это в MySQL? Я вышел с этим решением:

select * from articles
where exists (
    select * from tags
    join article_tag on article_tag.tag_id = tags.id
    where article_tag.article_id = articles.id
    and tags.tag = 'food'
) and exists (
    select * from tags
    join article_tag on article_tag.tag_id = tags.id
    where article_tag.article_id = articles.id
    and tags.tag = 'lifestyle'
) and exists (
    select * from tags
    join article_tag on article_tag.tag_id = tags.id
    where article_tag.article_id = articles.id
    and tags.tag = 'health'
)

Работает нормально, но выглядит как много повторений. Какой самый эффективный запрос для решения этой проблемы?

Ответы [ 4 ]

1 голос
/ 20 июня 2019
select a.*
from articles a 
join (
select articles.id
from articles
join article_tag on article_tag.article_id = articles.id
join tags on article_tag.tag_id = tags.id
where tags.tag in ('food','lifestyle','health')
group by articles.id
having SUM(CASE WHEN tags.tag = 'food' THEN 1 ELSE 0 END) >= 1
AND SUM(CASE WHEN tags.tag = 'lifestyle' THEN 1 ELSE 0 END) >= 1
AND SUM(CASE WHEN tags.tag = 'health' THEN 1 ELSE 0 END) >= 1) b on a.id = b.id
0 голосов
/ 21 июня 2019

Вот решение:

select a.*
from
  articles a
    inner join(
      select at.article_id
      from
        article_tag at
          inner join tags t
          on t.id = at.tag_id
      where t.tag in ('food', 'lifestyle', 'health')
      group by at.article_id
      having count(distinct t.tag) = 3
    ) at
    on at.article_id = a.id
0 голосов
/ 20 июня 2019
  select * from articles
where articles.id in (
    select article_tag.article_id from tags
    join article_tag on article_tag.tag_id = tags.id
    where article_tag.article_id = articles.id
    and tags.tag in ('food', 'lifestyle', 'health')        
    having count(*) >= 3    
)
0 голосов
/ 20 июня 2019

Вместо этого вы можете использовать условное агрегирование:

select a.*
from articles a join
     article_tag at
     on at.article_id = a.id join
     tags t
     on at.tag_id = t.id
where t.tag in ('food', 'lifestyle', 'health')
group by a.id
having count(*) = 3;

Это делает два разумных предположения:

  • id - это первичный ключ в article (или, по крайней мере, уникальный)
  • tag_id не имеет дубликатов
...