2010-06-08 97 views
3

我在MySQL數據庫,位置和標籤中有兩個表格,第三個表格LocationsTagsAssoc將兩個表格關聯起來,並將它們視爲多對多關係。在MySQL中選擇多對多關係

表結構如下:

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) 

因此,每個位置可以被標記與多個tagwords,並且每個tagword可以被標記到多個位置。

我想要做的是隻選擇標有的地方所有提供的標籤名稱。例如:

我希望所有標記爲「樹」和「鞦韆」的地點。應選擇位置「公園」,但位置「森林」不應該。

任何有識之士將不勝感激。謝謝!

回答

6

有兩種方法可以做到這一點。我更喜歡第一種方式,這是自聯接每個標籤:

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 = ?; 

其他方式也可以,但是用GROUP BY在MySQL中往往會引入臨時表,性能很慢:

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; 
+0

謝謝比爾。這是有道理的,並且運作良好。這兩個查詢在5個標記名查詢上的速度大致相同 - 我還沒有進行過任何測試。優秀的答案 - 謝謝。 – 2010-06-08 23:17:27

+0

我可以從經驗中證實,儘管後一種方法看起來更好(對我來說,無論如何),但對於大型表格,第一種方法可能會*效率更高。 – 2014-11-29 04:55:16

+1

@NickF,是的,我發現自連接方法有更好的性能。在我的演示文稿中查看結果[SQL Query Patterns,Optimized](http://www.slideshare.net/billkarwin/sql-query-patterns-optimized) – 2014-11-29 19:13:59

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,...) 
)