2009-10-12 80 views
11

讓我們以StackOverflow問題爲例。他們每個人都分配了多個標籤。如何建立一個算法,根據他們有多少通用標籤(按照通用標籤數量排序)來查找相關問題?根據通用標籤搜索相關項目的算法

現在我想不出任何比只選擇至少有一個公共標籤到數組中的所有問題都更好的方法,然後循環遍歷它們,爲每個項目分配一些通用標籤,然後對這個數組進行排序。

有沒有更聰明的做法呢?完美的解決方案將是一個單一的SQL查詢。

+0

如果您要查找的解決方案是SQL查詢,那麼瞭解您用於存儲標記的模式將會有幫助。 – Emily 2009-10-12 19:39:00

+0

@Emily:與中間表只是一個常規的多對多關係。 – serg 2009-10-12 21:00:16

回答

8

這可能是一樣糟糕,爲O(n^2),但它的工作原理:

create table QuestionTags (questionid int, tag int); 

select q1.questionid, q2.questionid, count(*) as commontags 
from QuestionTags q1 join QuestionTags q2 
where q1.tag = q2.tag and q1.questionid < q2.questionid 
group by q1.questionid, q2.questionid order by commontags desc; 
+0

謝謝你,我從閱讀這個優雅的例子中學到了一些有關SQL的知識。 – prismofeverything 2010-02-10 03:03:01

+0

你是不是指'q1.questionid!= q2.questionid'? – Xeoncross 2014-10-30 16:58:54

+1

@ Xeoncross:不,我的意思是<<。它避免了兩個自我匹配('!='也避免),並且每個對都列出兩次('!='不)。 – 2014-10-30 20:50:41

2

我沒有時間來優化WHERE IN()子句這是緩慢的死亡。我也沒有包括索引或指定引擎或排序規則,但這應該有希望做到這一點。請指出任何錯誤,因爲我並沒有實際測試過這個馬虎代碼:

CREATE TABLE question (
    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 
    title VARCHAR(255) NOT NULL, 
    question MEDIUMTEXT 
); 

CREATE TABLE question_rel_tag (
    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 
    question_id INT(11) UNSIGNED NOT NULL, 
    tag_id INT(11) UNSIGNED NOT NULL 
); 

CREATE TABLE tag (
    id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 
    name VARCHAR(20) NOT NULL 
); 

SELECT question.id, question.title, question.question, count(tag.id) AS `count` 
FROM question 
INNER JOIN question_rel_tag ON (question.id = question_rel_tag.question_id) 
INNER JOIN tag ON (question_rel_tag.tag_id = tag.id) 
WHERE question.id != YOUR_QUESTION_ID_HERE 
AND tag.id IN 
    (SELECT tag.id FROM question 
    INNER JOIN question_rel_tag ON (question.id = question_rel_tag.question_id) 
    INNER JOIN tag ON (question_rel_tag.tag_id = tag.id) 
    WHERE question.id = YOUR_QUESTION_ID_HERE) 
GROUP BY tag.id 
ORDER BY `count` DESC 
+0

您可能可以使用'WHERE ... tag.id EXISTS(...'而不是較慢的'IN(...)' – Xeoncross 2014-10-30 18:19:02

1

假設表Questions與主鍵列Id,表Tags與主鍵列Id,也和一個連接表QuestionTags使用複合主鍵QuestionIdTagId引用前兩個表的主鍵,以下查詢將提供所需的結果(在SQL Server 2005中)。

SELECT q1.Id AS Id1, q2.Id AS Id2, 
    (SELECT COUNT(*) FROM QuestionTags qt1 INNER JOIN QuestionTags qt2 
    ON qt1.QuestionId = q1.Id AND qt2.QuestionId = q2.Id AND qt1.TagId = qt2.TagId) AS TagCount 
FROM Questions q1 INNER JOIN Questions q2 ON q1.Id < q2.Id 
ORDER BY TagCount DESC 

這可以改進爲以下內容。

SELECT qt1.QuestionId AS Id1, qt2.QuestionId AS Id2, COUNT(*) AS TagCount 
FROM QuestionTags qt1 INNER JOIN QuestionTags qt2 
ON qt1.QuestionId < qt2.QuestionId AND qt1.TagId = qt2.TagId 
GROUP BY qt1.QuestionId, qt2.QuestionId 
ORDER BY TagCount DESC 
1

假設我有以下幾點:

加標籤實體的表:

Taggable

id, stuff

標籤的表:

Tag

id, tagName

一個加盟什麼標籤什麼Taggables

TagInfo

taggableId tagId

然後關聯表:

SELECT COUNT(Taggable_1.id) AS score, Taggable_1.id FROM dbo.Taggable INNER JOIN dbo.TagInfo ON dbo.Taggable.id = dbo.TagInfo.taggableId INNER JOIN dbo.TagInfo TagInfo_1 ON dbo.TagInfo.tagId = TagInfo_1.tagId INNER JOIN dbo.Taggable Taggable_1 ON TagInfo_1.taggableId = Taggable_1.id WHERE (dbo.Taggable.id = 1) AND (Taggable_1.id <> 1) GROUP BY Taggable_1.id

將加入標籤連接表對本身(所查詢問題的ID; 1在上面的sql)並計算結果得分。

1
select * 
    , count(t.*) as matched_tag_count 
    from questions q 
    join tags_to_questions t 
    on t.question_id = q.id 
    and t.tag_id in (select tag_id 
         from tags_to_questions 
        where question_id = ?) 
group 
    by q.id 
order 
    by matched_tag_count desc