2013-04-03 22 views
0

我有以下表格,有關這個問題:檢索項匹配的標籤,並同時檢索到的各項目走的所有標籤

project 
- projectId (PK) 
- projectTitle 
- projectDescription 
- etc.. 

tag 
- tagId 
- tagName 
- tagDescription 
- etc... 

project_tag 
- projectId (PK/FK -> project.projectId) 
- tagId (PK/FK -> tag.tagId) 

我實施類似於StackOverflow的具有標籤功能,在那個人能夠查看由一個或多個標籤標記的項目列表(在我的情況下是項目)。 我想要做的是選擇所有項目,至少要標記所有標記給我的查詢,但同時檢索所有與每個單獨項目一起使用的標記。

我現在的工作,但我覺得WHERE IN子句中的子查詢是非常低效的,因爲這可能會爲每一行執行,是正確的嗎?

SELECT 
    `project`.*, 
    GROUP_CONCAT(DISTINCT `tagName` ORDER BY `tagName` SEPARATOR ' ') as `tags` 
FROM 
    `project` 
JOIN 
    `project_tag` 
    USING (`projectId`) 
JOIN 
    `tag` 
    USING (`tagId`) 
WHERE 
    `projectId` IN (
     SELECT 
      `projectId` 
     FROM 
      `project_tag` 
     JOIN 
      `tag` 
      USING (`tagId`) 
     WHERE 
      `tagName` IN ('the', 'tags') 
     GROUP BY 
      `projectId` 
     HAVING 
      COUNT(DISTINCT `tagName`) = 2 # the amount of tags in the IN clause 
    ) 
GROUP BY 
    `projectId` 

有什麼辦法加入就tag,使得我能夠同時檢索JOIN版項目的所有標籤,而只有JOIN ING預計,(至少)匹配我喂到所有標籤查詢,而不必使用WHERE IN子句?

一個例子來說明結果,考慮這些示例項目:

projectId: 1, tags: php, cms, webdevelopment 
projectId: 2, tags: php, cms, ajax 
projectId: 3, tags: c#, cms, webdevelopment 

搜索標籤phpcms會產生(這些未格式化爲實際的MySQL查詢的結果,它只是說明相關數據):

projectId: 1, tags: php, cms, webdevelopment 
projectId: 2, tags: php, cms, ajax 

不只是:

projectId: 1, tags: php, cms 
projectId: 2, tags: php, cms 

回答

1

子查詢是不相關的(它可以被取出並自行執行而沒有錯誤),因此應該執行一次。

讓子查詢首先排除不匹配的項目,然後再反過來返回其他表可能會更高效。就像這樣: -

SELECT project.*, GROUP_CONCAT(DISTINCT tagName ORDER BY tagName SEPARATOR ' ') as tags 
FROM (SELECT projectId, COUNT(DISTINCT tagName) AS TagCount 
     FROM tag 
     INNER JOIN project_tag USING (tagId) 
     WHERE tagName IN ('the', 'tags') 
     GROUP BY projectId 
     HAVING TagCount = 2) Sub1 
INNER JOIN project ON Sub1.projectId = project.projectId 
INNER JOIN project_tag USING (projectId) 
INNER JOIN tag USING (tagId) 
GROUP BY projectId 

我假設你有一個tagName的索引。

+0

嘿,謝謝你的回答。 '子查詢不相關...';啊,是的,這是有道理的。就在你回答之前,我實際上提出了一個與你所建議的類似的查詢。但是我實際上沒有'tagName'上的索引,所以我現在添加了。該列是一個'VARCHAR(50)'(目前,仍處於開發階段)。你會建議我設置一個索引長度嗎? –

+0

我不會擔心在該索引上設置長度。要進行測試,請記住,如果IN子句中的標記數量大於MySQL可能會忽略該索引的可能標記數量(大約爲10%左右,這在使用有限的測試數據時非常容易發生)。 – Kickstart

+0

好的,謝謝你的寶貴見解。 –