2008-10-02 8 views
10

Supongamos que tengo una tabla de "etiquetas" con dos columnas: TagID y ContentID. Cada fila representa una etiqueta asignada a un contenido. Quiero una consulta que me dará la ContentID de cada pieza de contenido que está etiquetado con tagids 334, 338, y 342.consulta SQL: Simulación de una "Y" sobre varias filas en lugar de sub-consulta

La forma "fácil" de hacer esto sería ( pseudocódigo):

select contentid from tags where tagid = 334 and contentid in (
    select contentid from tags where tagid = 338 and contentid in (
     select contentid from tags where tagid = 342 
    ) 
) 

Sin embargo, mi instinto me dice que hay una manera mejor, más rápida, más extensible para hacer esto. Por ejemplo, ¿qué pasa si necesito encontrar la intersección de 12 etiquetas? Esto podría ser espantoso rápidamente. ¿Algunas ideas?

EDIT: Resulta que esto también está cubierto en this excellent blog post.

Respuesta

24
SELECT contentID 
FROM tags 
WHERE tagID in (334, 338, 342) 
GROUP BY contentID 
HAVING COUNT(DISTINCT tagID) = 3 


--In general 
SELECT contentID 
FROM tags 
WHERE tagID in (...) --taglist 
GROUP BY contentID 
HAVING COUNT(DISTINCT tagID) = ... --tagcount 
+0

Eso es todo. Aunque si tagid, contentid es único, entonces quizás DISTINCT no sea necesario en COUNT (DISTINCT tagID) – tzot

+0

Solución muy inteligente, pero necesitaría un ajuste en nuestra instalación de SQL2000. No podemos usar "tener" sin tener una agregación en la cláusula de selección. Sin embargo, es bastante fácil de evitar. –

+0

¿Esto funcionaría tanto en MySQL como en Oracle? –

1

La única alternativa que se me ocurre es:

select a.contentid from tags a 
inner join tags b on a.contentid = b.contentid and b.tagid=334 
inner join tags c on a.contentid = c.contentid and c.tagid=342 
where a.tagid=338 
-1

¿Qué tipo de SQL? MS SQL Server, Oracle, MySQL?

En SQL Server no esta equiparan a:

select contentid from tags where tagid IN (334,338,342) 
+1

No, eso le daría todos los artículos en las 3 etiquetas, quiere todos los artículos que tienen las mismas 3 etiquetas – albertein

+0

Sí, está totalmente muerto, David B lo clavó. – Meff

0

No sé si esto es mejor, pero podría ser más fácil de mantener

select contentid from tags where tagid = 334 
intersect 
select contentid from tags where tagid = 338 
intersect 
select contentid from tags where tagid = 342 

que tendría que construirlo dinámicamente que no sería tan malo como su solución original.

2

He aquí una solución que ha funcionado mucho más rápido que el para mí en una base de datos muy grande de objetos y etiquetas. Este es un ejemplo para una intersección de tres etiquetas. Simplemente cadenas de muchas uniones en la tabla de objetos-tag (objtags) para indicar el mismo objeto y establece los ID de etiqueta en la cláusula WHERE:

SELECT w0.objid 

FROM  objtags t0 
INNER JOIN objtags t1 ON t1.objid=t0.objid 
INNER JOIN objtags t2 ON t2.objid=t1.objid 

WHERE t0.tagid=512 
    AND t1.tagid=256 
    AND t2.tagid=128 

no tengo idea de por qué esto funciona más rápido. Fue inspirado por el código de búsqueda en el servidor de MusicBrainz. Al hacer esto en Postgres, generalmente obtengo una velocidad de ~ 8-10x sobre la solución HAVING COUNT(...).

+0

Está utilizando la intersección de conjuntos en lugar de la agregación para determinar si se aplican los tres valores. Menos paliza. Es maravilloso, ojalá hubiera pensado en eso. –

Cuestiones relacionadas