Select 只有来自 n:m 关系的 n 个关系的实体
Select only entities with n relations from an n:m relationship
我的数据库中有图像和标签的 m:n 关系,它使用交叉表对此进行建模。
Table imgs
包含的信息远不止 img_id
,但这是唯一标识图像所需的全部信息。
我想找到每个 img_id
同时具有 tagA 和 tagB(以及 tagC 等,我将构建此字符串,因此无论是两个还是十个标签都无关紧要)。
现在,我卡住了,当然首先你要加入 imgs
和 img_tags
和 tags
,为标签添加一个 where 子句;
SELECT *
FROM imgs
INNER JOIN img_tags ON imgs.img_id = img_tags.img_id
INNER JOIN tags ON img_tags.tag_id = tags.tag_id
WHERE tag = 'tagA' OR tag = 'tagB';
然后您将获得具有相同 imgs
信息的行,仅在 tag
和 tag_id
上有所不同。现在我应该能够计算那些,只针对那些出现的数量与提供的标签相同的数量 (Count(*) = n
),然后使用 group by
来聚合它们?但我不太明白。
如果可能相关,您可能会假设 img_tags 中的字段都是引用其他表的外键,但事实并非如此,它们没有以任何方式链接。
这是使用相关子查询的方法:
SELECT i.*
FROM imgs i
WHERE (
SELECT COUNT(*)
FROM img_tags it
INNER JOIN tags t ON it.tag_id = t.tag_id
WHERE i.img_id = it.img_id AND t.tag IN('tagA', 'tagB')
) = 2
这假设您的数据结构中没有重复标签。否则,您可以使用 COUNT(DISTINCT t.tag)
而不是 COUNT(*)
。
您还可以使用聚合:
SELECT i.id
FROM imgs i
INNER JOIN img_tags it ON i.img_id = it.img_id
INNER JOIN tags t ON it.tag_id = t.tag_id
WHERE t.tag IN('tagA', 'tagB')
GROUP BY i.id
HAVING COUNT(*) = 2
您可以像这样使用聚合:
SELECT i.*
FROM imgs i JOIN
img_tags it
ON i.img_id = it.img_id JOIN
tags t
ON it.tag_id = t.tag_id
WHERE tag IN ('tagA', 'tagB')
GROUP BY i.img_id
HAVING COUNT(*) = 2;
通过 i.img_id
聚合是安全的——并且受到 SQL 标准的支持——假设 img_id
是 table 中的主键。
如果涉及的标签不多,我会使用exists(如果你想排除一些标签,则不存在)
select *
from imgs
where
exists(select 1 from img_tags it where it.tag_id=(select tag_id from tags where tag='tagA') and it.img_id=imgs.img_id)
and exists(select 1 from img_tags it where it.tag_id=(select tag_id from tags where tag='tagB') and it.img_id=imgs.img_id);
特别是如果你最终想做更复杂的布尔表达式,例如(A 和(B 或非 C))。
我的数据库中有图像和标签的 m:n 关系,它使用交叉表对此进行建模。
Table imgs
包含的信息远不止 img_id
,但这是唯一标识图像所需的全部信息。
我想找到每个 img_id
同时具有 tagA 和 tagB(以及 tagC 等,我将构建此字符串,因此无论是两个还是十个标签都无关紧要)。
现在,我卡住了,当然首先你要加入 imgs
和 img_tags
和 tags
,为标签添加一个 where 子句;
SELECT *
FROM imgs
INNER JOIN img_tags ON imgs.img_id = img_tags.img_id
INNER JOIN tags ON img_tags.tag_id = tags.tag_id
WHERE tag = 'tagA' OR tag = 'tagB';
然后您将获得具有相同 imgs
信息的行,仅在 tag
和 tag_id
上有所不同。现在我应该能够计算那些,只针对那些出现的数量与提供的标签相同的数量 (Count(*) = n
),然后使用 group by
来聚合它们?但我不太明白。
如果可能相关,您可能会假设 img_tags 中的字段都是引用其他表的外键,但事实并非如此,它们没有以任何方式链接。
这是使用相关子查询的方法:
SELECT i.*
FROM imgs i
WHERE (
SELECT COUNT(*)
FROM img_tags it
INNER JOIN tags t ON it.tag_id = t.tag_id
WHERE i.img_id = it.img_id AND t.tag IN('tagA', 'tagB')
) = 2
这假设您的数据结构中没有重复标签。否则,您可以使用 COUNT(DISTINCT t.tag)
而不是 COUNT(*)
。
您还可以使用聚合:
SELECT i.id
FROM imgs i
INNER JOIN img_tags it ON i.img_id = it.img_id
INNER JOIN tags t ON it.tag_id = t.tag_id
WHERE t.tag IN('tagA', 'tagB')
GROUP BY i.id
HAVING COUNT(*) = 2
您可以像这样使用聚合:
SELECT i.*
FROM imgs i JOIN
img_tags it
ON i.img_id = it.img_id JOIN
tags t
ON it.tag_id = t.tag_id
WHERE tag IN ('tagA', 'tagB')
GROUP BY i.img_id
HAVING COUNT(*) = 2;
通过 i.img_id
聚合是安全的——并且受到 SQL 标准的支持——假设 img_id
是 table 中的主键。
如果涉及的标签不多,我会使用exists(如果你想排除一些标签,则不存在)
select *
from imgs
where
exists(select 1 from img_tags it where it.tag_id=(select tag_id from tags where tag='tagA') and it.img_id=imgs.img_id)
and exists(select 1 from img_tags it where it.tag_id=(select tag_id from tags where tag='tagB') and it.img_id=imgs.img_id);
特别是如果你最终想做更复杂的布尔表达式,例如(A 和(B 或非 C))。