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 等,我将构建此字符串,因此无论是两个还是十个标签都无关紧要)。

现在,我卡住了,当然首先你要加入 imgsimg_tagstags,为标签添加一个 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 信息的行,仅在 tagtag_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))。