Pregunta

Estoy implementando un sistema de etiquetado para un sitio web.Hay varias etiquetas por objeto y varios objetos por etiqueta.Esto se logra manteniendo una tabla con dos valores por registro, uno para los identificadores del objeto y la etiqueta.

Estoy buscando escribir una consulta para encontrar los objetos que coincidan con un conjunto determinado de etiquetas.Supongamos que tengo los siguientes datos (en formato [objeto] -> [etiquetas]*)

apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red

Si quiero hacer coincidir (rojo), debería obtener manzana y camión de bomberos.Si quiero combinar (fruta, comida), debo obtener (manzana, plátano).

¿Cómo escribo una consulta SQL y hago lo que quiero?

@Jeremy Ruten,

Gracias por tu respuesta.La notación utilizada se utilizó para proporcionar algunos datos de muestra: mi base de datos tiene una tabla con 1 ID de objeto y 1 etiqueta por registro.

En segundo lugar, mi problema es que necesito obtener todos los objetos que coincidan con todas las etiquetas.Sustituyendo tu OR por un AND así:

SELECT object WHERE tag = 'fruit' AND tag = 'food';

No produce resultados cuando se ejecuta.

¿Fue útil?

Solución

Dado:

  • tabla de objetos (id de clave principal)
  • tabla de etiquetas de objetos (claves externas objectId, tagid)
  • tabla de etiquetas (identificación de clave principal)

    SELECT distinct o.*
      from object o join objecttags ot on o.Id = ot.objectid
                    join tags t on ot.tagid = t.id
     where t.Name = 'fruit' or t.name = 'food';
    

Esto parece al revés, ya que desea y, pero el problema es que 2 etiquetas no están en la misma fila y, por lo tanto, y no produce nada, ya que 1 sola fila no puede ser a la vez una fruta y un alimento.Esta consulta generalmente generará duplicados, porque obtendrá 1 fila de cada objeto, por etiqueta.

Si realmente deseas hacer un y en este caso, necesitarás un group by, y un having count = <number of ors> en tu consulta por ejemplo.

  SELECT distinct o.name, count(*) as count
    from object o join objecttags ot on o.Id = ot.objectid
                  join tags t on ot.tagid = t.id
   where t.Name = 'fruit' or t.name = 'food'
group by o.name
  having count = 2;

Otros consejos

Oh Dios mío, puedo haber interpretado mal tu comentario original.

La forma más fácil de hacer esto en SQL sería tener tres tablas:

1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )

Luego, puede hacer prácticamente cualquier pregunta que desee sobre los datos de manera rápida, fácil y eficiente (siempre que indexe adecuadamente). Si quieres ponerte elegante, también puedes permitir etiquetas de varias palabras (hay una manera elegante y una forma menos elegante, creo).

Supongo que eso es lo que tienes, por lo que este SQL a continuación funcionará:

La forma literal:

    SELECT obj 
      FROM object
     WHERE EXISTS( SELECT * 
                     FROM tags 
                    WHERE tag = 'fruit' 
                      AND oid = object_id ) 
       AND EXISTS( SELECT * 
                     FROM tags 
                    WHERE tag = 'Apple'
                      AND oid = object_id )

También hay otras formas de hacerlo, como:

SELECT oid
  FROM tags
 WHERE tag = 'Apple'
INTERSECT
SELECT oid
  FROM tags
 WHERE tag = 'Fruit'

@Kyle: su consulta debería ser más como:

SELECT object WHERE tag IN ('fruit', 'food');

Su consulta buscaba filas en las que la etiqueta fuera fruta y comida, lo que es imposible de ver porque el campo solo puede tener un valor, no ambos al mismo tiempo.

Combina la sugerencia de Steve M. con la de Jeremy y obtendrás un solo registro con lo que estás buscando:

select object
from tblTags
where tag = @firstMatch
and (
       @secondMatch is null 
       or 
       (object in (select object from tblTags where tag = @secondMatch)
     )

Ahora, eso no escala muy bien, pero obtendrá lo que está buscando. Creo que hay una mejor manera de hacerlo para que pueda tener fácilmente un número N de elementos coincidentes sin un gran impacto en el código, pero actualmente se me escapa.

Recomiendo el siguiente esquema.

Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID

Con la siguiente consulta.

select distinct
    objectName
from
    ObjectTab ot
    join object o
        on o.objectID = ot.objectID
    join tabs t
        on t.tagID = ot.tagID
where
    tagName in ('red','fruit')

Sugeriría que su tabla tenga 1 etiqueta por registro, así:

 apple -> fruit
 apple -> red
 apple -> food
 banana -> fruit
 banana -> yellow
 banana -> food

Entonces podrías simplemente

 SELECT object WHERE tag = 'fruit' OR tag = 'food';

Si realmente quieres hacerlo a tu manera, puedes hacerlo así:

 SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top