我有这个数据库架构。我正在调用我正在标记对象的东西只是为了简单起见:
我一直在使用这样的查询来获取特定用户的对象以及对象的标签(如果用户标记了任何标签)。 如果您认为可以优化此功能,请随时告诉我。
@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%' ...
答案 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