Помощь с фильтрацией запросов «многие ко многим» - PullRequest
0 голосов
/ 18 января 2011

Я пытаюсь выяснить, как эффективно реализовать теги в моей базе данных.

Один из подходов состоит в том, чтобы иметь таблицу статей (скажем, Articles, ArtID int PK, ArtText varchar(max)) и таблицу тегов (скажем, Tags, TagID int PK, TagTitle varchar(15)). А затем я создам таблицу соединений, чтобы создать отношение «многие ко многим» между статьями и тегами (скажем, ArticleTags, ArtID int, TagID int (compound primary key).

Мой первый вопрос: как лучше написать запрос, чтобы найти все статьи, связанные с данным тегом? Я знаю о соединениях, если я хочу вернуть объединенные данные, но какой запрос наиболее эффективен, если я просто хочу узнать, какие строки статьи связаны с определенным тегом. В реальной жизни мне нужно будет найти статьи с несколькими тегами, и было бы неплохо также найти статьи, которые НЕ связаны с определенным тегом.

Мой второй вопрос о том, должна ли моя таблица тегов иметь int PK? Имеет ли смысл использовать TagTitle в качестве первичного ключа?

Ответы [ 2 ]

2 голосов
/ 18 января 2011

(1)

(a) Статьи с определенным тегом:

SELECT columns FROM Articles WHERE EXISTS 
  (SELECT null FROM ArticleTags at 
    WHERE at.ArtID = Articles.ArtID AND at.TagID=x)

(b) Статьи без определенного тега:

SELECT columns FROM Articles WHERE NOT EXISTS 
  (SELECT null FROM ArticleTags at 
    WHERE at.ArtID = Articles.ArtID AND at.TagID=x)

(2) Использование TagTitle быстрее, если вы просто получаете список тегов, связанных со статьей, но для большинства других возможных операций суррогат int будет быстрее.

0 голосов
/ 18 января 2011

1a) Мой первый вопрос: как лучше написать запрос, чтобы найти все статьи, связанные с данным тегом?

select ArtID  -- if only IDs are required
from ArticleTags
where TagID=1  -- or use the text (where tagtitle='x')

select A.*
from ArticleTags T inner join Articles A on A.ArtID = T.ArtID
where T.TagID=1

1b) В реальной жизни мне нужно будет найти статьи с несколькими тегами

-- has tags 1,3 and 8
select T1.ArtID
from ArticleTags T1
inner join ArticleTags T2 on T1.ArtID = T2.ArtID and T2.TagID = 3
inner join ArticleTags T3 on T1.ArtID = T3.ArtID and T3.TagID = 8
where T1.TagID=1
  -- or use the text (tagtitle='x') on each filter

В некоторых случаях эта форма работает быстрее. К этому или предыдущему можно присоединиться, чтобы получить записи статьи.

select ArtID
from (
    select T1.ArtID from ArticleTags T1 where T1.TagID=1
    union all
    select T2.ArtID from ArticleTags T2 where T2.TagID=3
    union all
    select T3.ArtID from ArticleTags T3 where T3.TagID=8
) X
group by ArtID
having count(ArtID) = 3

1c) и было бы неплохо также найти статьи, которые НЕ связаны с определенным тегом.

select A.*
from Articles A
left join ArticleTags T on T.ArtID = A.ArtID and T.TagTitle = 'nomatch'
where T.ArtID is null

2) Мой второй вопрос о том, должна ли моя таблица тегов иметь int PK? Имеет ли смысл использовать TagTitle в качестве первичного ключа?

Я твердо в лагере, который говорит, что каждая таблица должна иметь последовательный, бессмысленный целочисленный идентификатор. Это сокращает пространство хранения (FK из других таблиц), а поиск в диапазоне / поиск по диапазону всегда быстрее, чем varchar.

...