我致力于基于标签的搜索。我有三个表标签(id,name),tagXmedia(id,tag_id,media_id)和media(id,...)。 tagXmedia是标记和媒体表之间的映射表。这是一对多的关系。
我真的可以在如何创建“AND”类型的搜索方面使用一点方向。例如,我需要能够在媒体表中搜索与“home”和“hawaii”标签相关联的条目。
我已经尝试过MySQL存在,例如
SELECT
tam.media_id
FROM
tagXmedia tam
LEFT JOIN tag ON tag.id = tam.tag_id
WHERE
EXISTS (SELECT * FROM tag WHERE tag.name = "home")
AND EXISTS (SELECT * FROM tag WHERE tag.name = "hawaii")
对此的任何帮助都会非常感激。
答案 0 :(得分:8)
以下情况应该有效。
SELECT media_id
FROM tagXmedia
WHERE tag_id IN (SELECT id FROM tag WHERE name IN ('home','hawaii'))
GROUP BY media_id
HAVING COUNT(tag_id) = 2;
如果您希望它不仅仅匹配两个标签,您可以轻松添加它们。请记住更改HAVING
子句中的 2 。
我认为tagXmedia
中的所有行都是唯一的。如果不是,则必须将DISTINCT
添加到COUNT
部分。
答案 1 :(得分:1)
SELECT
media1.media_id
FROM
(
SELECT media_id FROM tagXmedia A INNER JOIN
(SELECT id tag_id FROM tag WHERE name='home') B
USING (tag_id)
) media1
INNER JOIN
(
SELECT media_id FROM tagXmedia C INNER JOIN
(SELECT id tag_id FROM tag WHERE name='hawaii') D
USING (tag_id)
) media2
USING (media_id)
;
确保tagXmedia中包含此索引:
ALTER TABLE tagXmedia ADD UNIQUE INDEX (tag_id,media_id);
这是一个测试用例:
drop database if exists tagmediatest;
create database tagmediatest;
use tagmediatest
CREATE TABLE media
(
id int not null auto_increment,
stuff varchar(20),
PRIMARY KEY (id)
);
INSERT INTO media (stuff) VALUES
('magazine'),('television'),('iphone'),
('ipad'),('IE9 Browser'),('radio');
CREATE TABLE tag
(
id int not null auto_increment,
name varchar(20),
PRIMARY KEY (id),
UNIQUE KEY (name)
);
INSERT INTO tag (name) VALUES
('away'),('home'),('jersery city'),('hawaii'),('nyc');
CREATE TABLE tagXmedia
(
id int not null auto_increment,
tag_id INT NOT NULL,
media_id INT NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (tag_id,media_id)
);
INSERT INTO tagXmedia (tag_id,media_id) VALUES
(1,1),(1,2),(1,3),(1,6),
(2,1),(2,2),(2,4),(2,5),
(3,5),(3,4),(3,3),(3,1),
(4,2),(4,3),(4,5),(4,6),
(5,2),(5,3),(5,5),(5,4);
SELECT
media1.media_id
FROM
(
SELECT media_id FROM tagXmedia A INNER JOIN
(SELECT id tag_id FROM tag WHERE name='home') B
USING (tag_id)
) media1
INNER JOIN
(
SELECT media_id FROM tagXmedia C INNER JOIN
(SELECT id tag_id FROM tag WHERE name='hawaii') D
USING (tag_id)
) media2
USING (media_id)
;
结果如下:
mysql> drop database if exists tagmediatest;
Query OK, 3 rows affected (0.09 sec)
mysql> create database tagmediatest;
Query OK, 1 row affected (0.00 sec)
mysql> use tagmediatest
Database changed
mysql> CREATE TABLE media
-> (
-> id int not null auto_increment,
-> stuff varchar(20),
-> PRIMARY KEY (id)
-> );
Query OK, 0 rows affected (0.05 sec)
mysql> INSERT INTO media (stuff) VALUES
-> ('magazine'),('television'),('iphone'),
-> ('ipad'),('IE9 Browser'),('radio');
Query OK, 6 rows affected (0.05 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> CREATE TABLE tag
-> (
-> id int not null auto_increment,
-> name varchar(20),
-> PRIMARY KEY (id),
-> UNIQUE KEY (name)
-> );
Query OK, 0 rows affected (0.08 sec)
mysql> INSERT INTO tag (name) VALUES
-> ('away'),('home'),('jersery city'),('hawaii'),('nyc');
Query OK, 5 rows affected (0.06 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> CREATE TABLE tagXmedia
-> (
-> id int not null auto_increment,
-> tag_id INT NOT NULL,
-> media_id INT NOT NULL,
-> PRIMARY KEY (id),
-> UNIQUE KEY (tag_id,media_id)
-> );
Query OK, 0 rows affected (0.06 sec)
mysql> INSERT INTO tagXmedia (tag_id,media_id) VALUES
-> (1,1),(1,2),(1,3),(1,6),
-> (2,1),(2,2),(2,4),(2,5),
-> (3,5),(3,4),(3,3),(3,1),
-> (4,2),(4,3),(4,5),(4,6),
-> (5,2),(5,3),(5,5),(5,4);
Query OK, 20 rows affected (0.05 sec)
Records: 20 Duplicates: 0 Warnings: 0
mysql> SELECT
-> media1.media_id
-> FROM
-> (
-> SELECT media_id FROM tagXmedia A INNER JOIN
-> (SELECT id tag_id FROM tag WHERE name='home') B
-> USING (tag_id)
-> ) media1
-> INNER JOIN
-> (
-> SELECT media_id FROM tagXmedia C INNER JOIN
-> (SELECT id tag_id FROM tag WHERE name='hawaii') D
-> USING (tag_id)
-> ) media2
-> USING (media_id)
-> ;
+----------+
| media_id |
+----------+
| 2 |
| 5 |
+----------+
2 rows in set (0.00 sec)
mysql>
请注意,tag_id 2和4位于media_id 2和5中。这就是查询有效的原因。
答案 2 :(得分:1)
@ kba的答案是正确的,但您也可以使用JOIN来做到这一点,这可能更有效。
SELECT media_id
FROM tagXmedia
LEFT JOIN tag ON tag_id = tag.id
WHERE tag.name IN ('home', 'hawaii')
GROUP BY media_id
HAVING COUNT(tag_id) = 2;
我有一个类似的问题,我想要的不仅仅是media_id
而是实际的对象,并希望传递任意逗号分隔的标签列表。这是使用存储过程的完整解决方案:
CREATE PROCEDURE FindByTag(IN _tags VARCHAR(256))
BEGIN
DECLARE _length INT;
-- Get the length of the list
SET _tags = TRIM(BOTH ',' FROM _tags);
SET _length = LENGTH(_tags) - LENGTH(REPLACE(_tags, ',', '')) + 1;
-- Find media
SELECT * FROM media
WHERE id IN (
SELECT media_id FROM tagXmedia
LEFT JOIN tag ON tag_id = tag.id
WHERE FIND_IN_SET(tag.name, _tags)
GROUP BY media_id
HAVING COUNT(tag_id) = _length
)
END
答案 3 :(得分:0)
尝试此查询:
SELECT
T1.media_id
FROM
tagXmedia as T1
INNER JOIN media as T2
ON T1.media_id =T2.id
INNER JOIN tag as T3
ON T1.id = T3.tag_id AND T3.name IN ('home','hawaii')
GROUP BY T1.media_id