2012-01-06 21 views
8

Estoy trabajando en una búsqueda basada en etiquetas. Tengo tres tablas etiqueta (id, nombre), tagXmedia (id, tag_id, media_id) y multimedia (id, ...). tagXmedia es la tabla de asignación entre la etiqueta y las tablas de medios. Esta es una relación de uno a muchos.Consulta de búsqueda de etiquetas múltiples

Me vendría bien un poco de orientación sobre la forma de crear una "Y" tipo de búsqueda. Por ejemplo, necesito poder buscar una entrada en la tabla de medios asociada con las etiquetas "home" y "hawaii".

he experimentado con MySQL existe como

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

realmente sería apreciada Cualquier ayuda en esto.

Respuesta

8

El siguiente debería funcionar.

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; 

Si desea que se conectan a más de sólo dos etiquetas, puede agregar fácilmente. Sólo recuerde que debe cambiar el en la cláusula HAVING.

asumí todas las filas de tagXmedia son únicos. En caso de que no lo sean, deberá agregar DISTINCT a la parte COUNT.

+0

que funciona, me encontré con otro ejemplo, pero el suyo es un poco más concisa. ¡Gracias! –

0

Intentar esta consulta:

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

Tus uniones internas anidadas no te permitirán acceder a 'T3.tag_id'. – kba

+0

¿Cuál es el problema? Ist join interno obtendrá archivos multimedia y 2nd join obtendrá etiqueta con el nombre "('home', 'hawaii')" –

+0

¿En qué versión de MySQL lo ha probado? – kba

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

Asegúrese de que tiene este índice en tagXmedia:

ALTER TABLE tagXmedia ADD UNIQUE INDEX (tag_id,media_id); 

Aquí es un caso de prueba:

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

Aquí es el resultado:

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> 

Tenga en cuenta que tag_id 2 y 4 residen en media_id 2 y 5. Esta es la razón por la cual la consulta funciona.

+0

Esto devolverá todo lo que tenga 'home' o 'hawaii'. – kba

+0

Tienes razón. Olvidé 'USING (media_id)'. Actualizaré mi respuesta. – RolandoMySQLDBA

1

@ respuesta de KBA es correcta pero también se puede hacer esto con un JOIN que es probablemente más eficiente.

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; 

que tenía un problema similar donde quería llegar, pero no sólo el objeto real media_id y quería pasar en coma arbitraria listas de etiquetas separadas. Aquí está mi solución completa mediante un procedimiento almacenado:

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

dices "probablemente sea más eficiente", pero ¿realizaste una comparación de esta consulta con @ kba's? – Daishi

+0

bien, tengo mi respuesta desde estas páginas https://stackoverflow.com/questions/2577174/join-vs-sub-query y https://stackoverflow.com/questions/141278/subqueries-vs-joins. Parece que 'JOIN' es el camino a seguir. – Daishi

Cuestiones relacionadas