【问题标题】:Retrieve items matching tags, and simultaneously retrieve all tags that go along with each item检索与标签匹配的项目,并同时检索与每个项目一起出现的所有标签
【发布时间】:2013-03-25 01:12:57
【问题描述】:

我有以下与这个问题相关的表格:

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 以便我能够同时检索JOINed 项目的所有 标签,而只有JOINing 项目(至少) 匹配我提供给查询的所有标签,而不必使用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

【问题讨论】:

    标签: mysql join tags where-in


    【解决方案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 上有一个索引。

    【讨论】:

    • 您好,谢谢您的回答。 The sub query is non correlated...;啊,是的,这是有道理的。就在你回答之前,我实际上想出了一个与你建议的类似的查询。但实际上我在tagName 上没有索引,所以我现在添加了它。该专栏是VARCHAR(50)(目前仍处于开发阶段)。您是否建议我也设置一个索引长度?
    • 我不会担心在该索引上设置长度。对于测试,请记住如果 IN 子句中的标签数量与 MySQL 可能忽略索引的可能标签数量相比很大(大约 10% 左右,这在使用有限的测试数据时很容易命中)。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多