从n:m关系中仅选择具有n关系的实体

问题描述

我的数据库中的图像和标签具有m:n的关系,该关系使用交叉表对此建模。 表imgs包含的信息远远不止img_id,但这是唯一标识图像所需的全部信息。

我想找到每个同时包含tagA和tagB(以及tagC等)的img_id,我将构建此字符串,因此,无论它是两个标签还是十个标签,都没有关系。

enter image description here

现在,我遇到的困难是,首先您要将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是表中的主键,按img_id进行聚合是安全的-并且受到SQL标准的支持。

,

如果涉及的标签不多,我将为此使用现存的(或者如果您要排除一些标签则不存在)

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)。