2016-03-15 78 views
6

我的數據庫中有兩個實體與多對多關係相關。我想知道什麼是最好的方式來列出哪些實體最相似的基礎上呢?基於多對多的關係來匹配相似的實體

我試着用交叉計數(*),但查詢需要太長的時間才能在我的數據庫中的每個條目上運行(大約有20k條記錄)。運行我寫的查詢時,CPU使用率跳至100%,數據庫出現鎖定問題。

下面是一些代碼顯示我已經試過:

我的表看東西沿着這些路線:

/* 20k records */ 
create table Movie(
    Id INT PRIMARY KEY, 
    Title varchar(255) 
); 

/* 200-300 records */ 
create table Tags(
    Id INT PRIMARY KEY, 
    Desc varchar(255) 
); 

/* 200,000-300,000 records */ 
create table TagMovies(
    Movie_Id INT, 
    Tag_Id INT, 
    PRIMARY KEY (Movie_Id, Tag_Id), 
    FOREIGN KEY (Movie_Id) REFERENCES Movie(Id), 
    FOREIGN KEY (Tag_Id) REFERENCES Tags(Id), 
); 

(這工作,但它是非常慢) 這是查詢我寫了試圖列出它們: 通常我還會過濾頂部1 &添加where子句以獲取特定的一組相關數據。

SELECT 
    bk.Id, 
    rh.Id 
FROM 
    Movies bk 
    CROSS APPLY (
     SELECT TOP 15 
      b.Id, 
      /* Tags Score */ 
      (
      SELECT COUNT(*) FROM (
       SELECT x.Tag_Id FROM TagMovies x WHERE x.Movie_Id = bk.Id 
       INTERSECT 
       SELECT x.Tag_Id FROM TagMovies x WHERE x.Movie_Id = b.Id 
       ) Q1 
      ) 
      as Amount 
     FROM 
      Movies b 
     WHERE 
      b.Id <> bk.Id 
     ORDER BY Amount DESC 
    ) rh 

說明: 電影有標籤,用戶可以得到試圖找到類似於他們基於具有類似的標籤其他電影選擇了一個電影。

回答

4

嗯...只是一個想法,但也許我不明白... 這個查詢應該由標籤爲一個給定的電影ID返回最匹配的電影:

SELECT m.id, m.title, GROUP_CONCAT(DISTINCT t.Descr SEPARATOR ', ') as tags, count(*) as matches 
FROM stack.Movie m 
LEFT JOIN stack.TagMovies tm ON m.Id = tm.Movie_Id 
LEFT JOIN stack.Tags t ON tm.Tag_Id = t.Id 
WHERE m.id != 1 
AND tm.Tag_Id IN (SELECT Tag_Id FROM stack.TagMovies tm WHERE tm.Movie_Id = 1) 
GROUP BY m.id 
ORDER BY matches DESC 
LIMIT 15; 

編輯: 我剛剛意識到它的M $ SQL ...但也許可以做類似的事情...

+0

謝謝。我最終用連接寫了一個類似的解決方案。 – newb

+0

性能如何提高? 順便說一句。我認爲,你可以通過索引獲得一些速度標籤ID – barat

+0

1小時 - > 40秒 – newb

1

你應該決定一個命名約定,並堅持下去。表格是單數還是複數名詞?我不想進入那場辯論,但挑一個或另一個。

無法訪問您的數據庫我不知道這將如何執行。這只是我的頭頂。您還可以通過M.id值限制此值,以找到單個電影的最佳匹配,我認爲這可以提高相當多的性能。

此外,TOP x應該讓你得到x最接近的匹配。

SELECT 
    M.id, 
    M.title, 
    SM.id AS similar_movie_id, 
    SM.title AS similar_movie_title, 
    COUNT(*) AS matched_tags 
FROM 
    Movie M 
INNER JOIN TagsMovie TM1 ON TM1.movie_id = M.movie_id 
INNER JOIN TagsMovie TM2 ON 
    TM2.tag_id = TM1.tag_id AND 
    TM2.movie_id <> TM1.movie_id 
INNER JOIN Movie SM ON SM.movie_id = TM2.movie_id 
GROUP BY 
    M.id, 
    M.title, 
    SM.id AS similar_movie_id, 
    SM.title AS similar_movie_title 
ORDER BY 
    COUNT(*) DESC 
+0

據我所知,上述解決方案甚至不適用於T-SQL,您的解決方案是唯一有效的解決方案。另外,另一個解決方案只能在你一次全部獲得時才縮小到1個標題。所以,不知道爲什麼另一個解決方案得到了所有要點。 – Ralph