我在MySQL数据库中有两个表,Locations和Tags,以及第三个表LocationsTagsAssoc,它们将这两个表关联起来并将它们视为多对多关系。
表结构如下:
Locations
---------
ID int (Primary Key)
Name varchar(128)
LocationsTagsAssoc
------------------
ID int (Primary Key)
LocationID int (Foreign Key)
TagID int (Foreign Key)
Tags
----
ID int (Primary Key)
Name varchar(128)
因此,每个位置都可以使用多个标记字进行标记,每个标记字都可以标记为多个位置。
我想要做的是仅选择标有 所有提供的标签名称的位置 。例如:
我想要所有标有“树”和“秋千”的位置。应选择位置“公园”,但不应选择位置“森林”。
任何见解都将受到赞赏。谢谢!
答案 0 :(得分:7)
有两种方法可以做到这一点。我更喜欢第一种方式,即为每个标签自我加入:
SELECT l.*
FROM Locations l
JOIN LocationsTagsAssoc a1 ON a1.LocationID = l.ID
JOIN Tags t1 ON a1.TagID = t1.ID AND t1.Name = ?
JOIN LocationsTagsAssoc a2 ON a2.LocationID = l.ID
JOIN Tags t2 ON a2.TagID = t2.ID AND t2.Name = ?
JOIN LocationsTagsAssoc a3 ON a3.LocationID = l.ID
JOIN Tags t3 ON a3.TagID = t3.ID AND t3.Name = ?;
另一种方式也有效,但在MySQL中使用GROUP BY
往往会产生临时表并且性能很慢:
SELECT l.*
FROM Locations l
JOIN LocationsTagsAssoc a ON a.LocationID = l.ID
JOIN Tags t ON a.TagID = t.ID
WHERE t.Name IN (?, ?, ?)
GROUP BY l.ID
HAVING COUNT(*) = 3;
答案 1 :(得分:0)
您需要的位置不存在LocationsTagsAssoc表中未显示给定标记的位置。
您可以使用IN()指定给定标记,如下所示,或者通过连接到包含它们的另一个表。
即
SELECT l.*
FROM Locations AS l
WHERE NOT EXISTS (
SELECT NULL FROM Tags AS t
WHERE NOT EXISTS (
SELECT NULL FROM LocationsTagsAssoc AS lt
WHERE lt.LocationId = l.ID
AND lt.TagID = t.ID
)
AND t.ID IN (1, 2, 3,...)
)