讓我們以StackOverflow問題爲例。他們每個人都分配了多個標籤。如何建立一個算法,根據他們有多少通用標籤(按照通用標籤數量排序)來查找相關問題?根據通用標籤搜索相關項目的算法
現在我想不出任何比只選擇至少有一個公共標籤到數組中的所有問題都更好的方法,然後循環遍歷它們,爲每個項目分配一些通用標籤,然後對這個數組進行排序。
有沒有更聰明的做法呢?完美的解決方案將是一個單一的SQL查詢。
讓我們以StackOverflow問題爲例。他們每個人都分配了多個標籤。如何建立一個算法,根據他們有多少通用標籤(按照通用標籤數量排序)來查找相關問題?根據通用標籤搜索相關項目的算法
現在我想不出任何比只選擇至少有一個公共標籤到數組中的所有問題都更好的方法,然後循環遍歷它們,爲每個項目分配一些通用標籤,然後對這個數組進行排序。
有沒有更聰明的做法呢?完美的解決方案將是一個單一的SQL查詢。
這可能是一樣糟糕,爲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;
謝謝你,我從閱讀這個優雅的例子中學到了一些有關SQL的知識。 – prismofeverything 2010-02-10 03:03:01
你是不是指'q1.questionid!= q2.questionid'? – Xeoncross 2014-10-30 16:58:54
@ Xeoncross:不,我的意思是<<。它避免了兩個自我匹配('!='也避免),並且每個對都列出兩次('!='不)。 – 2014-10-30 20:50:41
我沒有時間來優化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
您可能可以使用'WHERE ... tag.id EXISTS(...'而不是較慢的'IN(...)' – Xeoncross 2014-10-30 18:19:02
假設表Questions
與主鍵列Id
,表Tags
與主鍵列Id
,也和一個連接表QuestionTags
使用複合主鍵QuestionId
和TagId
引用前兩個表的主鍵,以下查詢將提供所需的結果(在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
假設我有以下幾點:
加標籤實體的表:
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)並計算結果得分。
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
如果您要查找的解決方案是SQL查詢,那麼瞭解您用於存儲標記的模式將會有幫助。 – Emily 2009-10-12 19:39:00
@Emily:與中間表只是一個常規的多對多關係。 – serg 2009-10-12 21:00:16