许多对许多人查询标记对象

时间:2012-12-18 03:12:35

标签: mysql

我有这个数据库架构。我正在调用我正在标记对象的东西只是为了简单起见:

用户

  • USER_ID
  • 登录

用户到对象表(多对多)

  • USER_ID
  • 的object_id

对象表

  • 的object_id
  • object_stuff

使用user_id表标记的对象(多列有额外列)

  • USER_ID
  • 的object_id
  • TAG_ID

标签表

  • TAG_ID
  • TAG_NAME

我一直在使用这样的查询来获取特定用户的对象以及对象的标签(如果用户标记了任何标签)。 如果您认为可以优化此功能,请随时告诉我

@user_id = 'whatever user_id I want';
SELECT 
    o.object_id AS object_id,
    o.object_stuff AS object_stuff,
    IF (tags.tag_name IS NOT NULL, GROUP_CONCAT(tags.tag_name SEPARATOR '|'), 'N/A') AS tags
FROM  object_table AS o
LEFT OUTER JOIN  obj_to_users_table AS o2u ON o.object_id = o2u.object_id
LEFT OUTER JOIN  users AS u ON u.user_id = o2u.user_id 
LEFT OUTER JOIN  obj_to_tag_users AS o2tu ON o.object_id = o2tu.object_id AND u.user_id = o2tu.user_id
LEFT OUTER JOIN  tags AS t ON t.tag_id = o2t.tag_id 
WHERE u.user_id = @user_id GROUP BY o.object_id

哪会产生这样的结果:

object_id     object_stuff        tags
1             stuff1...           tag1|tag2|tag3
2             stuff2...           tag4|tag6|tag1
3             stuff3...           tag7|tag2|tag5

我的问题是,我想,例如,搜索标记为'tag2'的对象,我应该得到:

object_id     object_stuff        tags
1             stuff1...           tag1|tag2|tag3
3             stuff3...           tag4|tag2|tag1

但相反,我丢失了伴随该对象的其他标签:

object_id     object_stuff        tags
1             stuff1...           tag2
3             stuff3...           tag2

如何修改我的WHERE子句,以便我可以将对象标记为'tag2',同时保留对象在结果集中的其他标记?

... WHERE u.user_id = @user_id AND t.tag_name LIKE '%tag2%' ...

1 个答案:

答案 0 :(得分:1)

首先,计算连接结果。

接下来,针对这些连接结果执行搜索以保留标记的分组:

SELECT * 
FROM
(
    SELECT 
        o.object_id AS object_id,
        o.object_stuff AS object_stuff,
        IF (tags.tag_name IS NOT NULL, 
            GROUP_CONCAT(tags.tag_name SEPARATOR '|'), 'N/A') AS tags
    FROM  object_table AS o
    LEFT OUTER JOIN  obj_to_users_table AS o2u 
        ON o.object_id = o2u.object_id
    LEFT OUTER JOIN  users AS u 
        ON u.user_id = o2u.user_id 
    LEFT OUTER JOIN  obj_to_tag_users AS o2tu 
        ON o.object_id = o2tu.object_id AND u.user_id = o2tu.user_id
    LEFT OUTER JOIN  tags AS t ON t.tag_id = o2t.tag_id 
    WHERE u.user_id = @user_id 
    GROUP BY o.object_id
) sub
WHERE sub.tags LIKE '%tag2%'

修改


以下是使用EXISTS的示例。我还将LEFT JOIN切换为INNER JOIN,因为搜索特定代码应该会消除任何objects而不会tags

虽然这个查询看起来更复杂,但它应该有更好的机会使用任何可用的索引。

我还提到使用一组额外的INNER JOIN来执行相同类型的查询,但是由于您仍然需要使用子查询来使用{{1除去额外的DISTINCT引入的任何可能的重复项...所以我建议使用JOIN代替......

EXISTS