Лучший запрос SQL 2005 для получения связанных элементов? - PullRequest
3 голосов
/ 27 июня 2009

У меня есть небольшой видео-сайт, где я хочу получить похожие видео на основе самых подходящих тегов. Какой будет лучший запрос MSSQL 2005 для получения похожих видео?

Был бы также признателен запрос LINQ.


Схема:

CREATE TABLE Videos
    (VideoID bigint not null , 
    Title varchar(100) NULL, 
    isActive bit NULL  )

CREATE TABLE Tags
    (TagID bigint not null , 
    Tag varchar(100) NULL )

CREATE TABLE VideoTags
    (VideoID bigint not null , 
    TagID bigint not null )

Каждое видео может иметь несколько тегов. Теперь я хочу получить похожие видео на основе тегов, но только те видео, которые соответствуют большинству тегов. Наиболее подходящие видео должны располагаться сверху, а менее подходящие - снизу, если не найдено ни одного тега, видео не должно возвращаться.

Также я хочу знать, что приведенная выше схема в порядке, если я скажу более миллиона видео и 10-20 тегов для каждого видео.

Ответы [ 4 ]

1 голос
/ 28 июня 2009

Вот sql

SELECT v.VideoID, v.Title, v.isActive
FROM Videos v
  JOIN 
(
  SELECT vt.VideoID, Count(*) as MatchCount
  FROM VideoTags vt
  WHERE vt.TagID in
  (
    SELECT TagID
    FROM Tags t
    WHERE t.Tag in ('horror', 'scifi')
  )
  GROUP BY vt.VideoID
) as sub
  ON v.VideoID = sub.VideoID
ORDER BY sub.MatchCount desc

А вот и Линк.

List<string> TagList = new List<string>() {"horror", "scifi"};

  //find tag ids.
var tagQuery =
  from t in db.Tags
  where TagList.Contains(t.Tag))
  select t.TagID

  //find matching video ids, count matches for each
var videoTagQuery =
  from vt in db.VideoTags
  where tagQuery.Contains(vt.TagID)
  group vt by vt.VideoID into g
  select new { VideoID = g.Key, matchCount = g.Count;

  //fetch videos where matches were found
  //ordered by the number of matches
var videoQuery =
  from v in db.Videos
  join x in videoTagQuery on v.VideoID equals x.VideoID
  orderby x.matchCount
  select v
  //hit the database and pull back the results
List<Video> result = videoQuery.ToList();

Ой, подождите - у вас нет списка тегов, у вас есть видео и вы хотите видео с похожими тегами. Ok:

SELECT v.VideoID, v.Title, v.isActive
FROM Videos v
  JOIN 
(
  SELECT vt.VideoID, Count(*) as MatchCount
  FROM VideoTags vt
  WHERE vt.TagID in
  (
    SELECT TagID
    FROM VideoTags vt2
    WHERE vt2.VideoID = @VideoID
  )
  GROUP BY vt.VideoID
) as sub
  ON v.VideoID = sub.VideoID
ORDER BY sub.MatchCount desc

И Linq такой же, кроме изменений запроса тега

int myVideoID = 4

  //find tag ids.
var tagQuery =
  from t in db.VideoTags
  where t.VideoID = myVideoID
  select t.TagID
0 голосов
/ 27 июня 2009

В этом запросе будут отсортированы видеоролики по количеству связанных тегов (в порядке убывания):

select video.videoId, Title, count(*) nroOfTags
from videos, VideoTags
where
videoTags.videoid = videos.videoID 
and tagId in ('horror','action','adventure')
group by video.videoId, Title
order by count(*) desc

Что касается модели данных, все в порядке. Это будет работать хорошо, если все правильные индексы на месте.

0 голосов
/ 27 июня 2009

Я бы сделал несколько изменений в DDL:

CREATE TABLE [Tags](
    [TagID] [bigint] IDENTITY(1,1) NOT NULL,
    [Tag] [nvarchar](100) NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [TagID] ASC
),
 CONSTRAINT [UC_Tags] UNIQUE NONCLUSTERED 
(
    [Tag] ASC
)
)

GO

CREATE TABLE [Videos](
    [VideoID] [bigint] IDENTITY(1,1) NOT NULL,
    [Title] [nvarchar](100) NOT NULL,
    [isActive] [bit] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [VideoID] ASC
),
 CONSTRAINT [UC_Videos] UNIQUE NONCLUSTERED 
(
    [Title] ASC
)
)

GO

CREATE TABLE [VideoTags](
    [VideoID] [bigint] NOT NULL,
    [TagID] [bigint] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [VideoID] ASC,
    [TagID] ASC
)
)

GO

ALTER TABLE [VideoTags]  WITH CHECK ADD FOREIGN KEY([TagID])
REFERENCES [Tags] ([TagID])
GO

ALTER TABLE [VideoTags]  WITH CHECK ADD FOREIGN KEY([VideoID])
REFERENCES [Videos] ([VideoID])
GO
  1. Я бы сделал текстовые столбцы nvarchar. Облегчает отслеживание "иностранных" фильмов.
  2. Я бы сделал столбцы идентификаторов IDENTITY и сделал бы их первичными ключами
  3. Я бы назначил внешние ключи
  4. Я бы сделал столбцы Tag и Title уникальными. Вы не хотите дублировать заголовки или теги
  5. Я бы сделал все эти столбцы необнуляемыми. Нет смысла иметь видео или тег с неизвестным именем, а видео является активным и неактивным, никогда «возможно» или «неизвестно».
  6. Я добавил первичный ключ в VideoTags для предотвращения дублирования.

Для запроса SQL я бы попробовал следующее. Я не могу быть уверен, что это то, что вы хотите без тестовых данных:

;
WITH VIDEO_TAG_COUNTS(VideoID,TagCount)
AS
(
    SELECT v.VideoID, COUNT(*)
    FROM Videos V
    INNER JOIN VideoTags VT ON V.VideoID = VT.VideoID
    GROUP BY V.VideoID
)
SELECT V.VideoID, V.Title
FROM Videos V 
INNER JOIN VIDEO_TAG_COUNTS VTC ON V.VideoID = VTC.VideoID
WHERE V.isActive = 1
ORDER BY VTC.TagCount
0 голосов
/ 27 июня 2009

Что-то вроде того, что вы после?

String horror = "Horror";
String thriller = "Thriller";

var results =
    from v in db.Videos
    join vt in db.VideoTags on v.VideoId equals vt.VideoId
    join t in db.Tags on vt.TagId equals t.TagId
    where
        t.Tag == horror || t.Tag == thriller
    select v;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...