Pregunta

Además de esta pregunta consulta SQL que proporciona distintos resultados que coinciden con varias columnas la cual tenía una solución muy clara, me preguntaba cómo se vería el siguiente paso:

 DOCUMENT_ID |     TAG
----------------------------
   1        |   tag1
   1        |   tag2
   1        |   tag3
   2        |   tag2
   3        |   tag1
   3        |   tag2
   4        |   tag1
   5        |   tag3

Entonces, para obtener todos los document_ids que tienen las etiquetas 1 y 2, realizaríamos una consulta como esta:

SELECT document_id
FROM table
WHERE tag = 'tag1' OR tag = 'tag2'
GROUP BY document_id
HAVING COUNT(DISTINCT tag) = 2

Ahora, lo que sería interesante saber es cómo obtendríamos todos los document_ids distintos que tienen las etiquetas 1 y 2, y además los identificadores que tienen la etiqueta 3. Podríamos imaginarnos haciendo la misma consulta y realizando una unión entre ellos:

SELECT document_id
FROM table
WHERE tag = "tag1" OR tag = "tag2"
GROUP BY document_id
HAVING COUNT(DISTINCT tag) = 2
UNION
SELECT document_id
FROM table
WHERE tag = "tag3"
GROUP BY document_id

Pero me preguntaba si con esa condición agregada, podríamos pensar en otra consulta inicial. Me imagino tener muchos " uniones " así con diferentes etiquetas y conteos de etiquetas. ¿No sería muy malo en términos de desempeño crear cadenas de sindicatos como esa?

¿Fue útil?

Solución

Esto todavía usa uniones de tipo pero puede ser más fácil de leer y controlar. Estoy realmente interesado en la velocidad de esta consulta en un conjunto de datos de gran tamaño, así que hágame saber qué tan rápido es. Cuando puse su pequeño conjunto de datos, tomó 0.0001 segundos.

SELECT DISTINCT (dt1.document_id)
FROM 
  document_tag dt1,
  (SELECT document_id
    FROM document_tag
    WHERE tag =  'tag1'
  ) AS t1s,
  (SELECT document_id
    FROM document_tag
    WHERE tag =  'tag2'
  ) AS t2s,
  (SELECT document_id
    FROM document_tag
    WHERE tag =  'tag3'
  ) AS t3s
WHERE
  (dt1.document_id = t1s.document_id
  AND dt1.document_id = t2s.document_id
  )
  OR dt1.document_id = t3s.document_id

Esto facilitará la adición de nuevos parámetros porque ya ha especificado el conjunto de resultados para cada etiqueta.

Por ejemplo, agregando:

OR dt1.document_id = t2s.document_id

hasta el final también recogerá document_id 2

Otros consejos

Es posible hacer esto dentro de una sola, sin embargo, deberá promocionar su cláusula WHERE en la cláusula have para usar un disyuntivo.

Usted tiene razón, se volverá más y más lento a medida que agregue nuevas etiquetas que desee buscar en cláusulas UNION adicionales. Cada cláusula UNION es una consulta adicional que debe planificarse y ejecutarse. Además, no podrás ordenar cuando hayas terminado.

Estás buscando una técnica básica de almacenamiento de datos. Primero, permítame recrear su esquema con una tabla adicional.

create table a (document_id int, tag varchar(10));

insert into a values (1, 'tag1'), (1, 'tag2'), (1, 'tag3'), (2, 'tag2'), 
                     (3, 'tag1'), (3, 'tag2'), (4, 'tag1'), (5, 'tag3');

create table b (tag_group_id int, tag varchar(10));

insert into b values (1, 'tag1'), (1, 'tag2'), (2, 'tag3');

La tabla b contiene " grupos de etiquetas " ;. El grupo 1 incluye tag1 y tag2, mientras que el grupo 2 contiene tag3.

Ahora puede modificar la tabla b para representar la consulta en la que está interesado. Cuando esté listo para realizar la consulta, cree tablas temporales para almacenar datos agregados:

create temporary table c 
(tag_group_id int, count_tags_in_group int, tags_in_group varchar(255));

insert into c
select 
    tag_group_id,
    count(tag),
    group_concat(tag)
from b
group by tag_group_id;

create temporary table d (document_id int, tag_group_id int, document_tag_count int);

insert into d
select
    a.document_id,
    b.tag_group_id,
    count(a.tag) as document_tag_count
from a
inner join b on a.tag = b.tag
group by a.document_id, b.tag_group_id;

Ahora c contiene la cantidad de etiquetas para el grupo de etiquetas, y d contiene la cantidad de etiquetas que cada documento tiene para cada grupo de etiquetas. Si una fila en c coincide con una fila en d, entonces eso significa que el documento tiene todas las etiquetas en ese grupo de etiquetas.

select 
    d.document_id as "Document ID",
    c.tags_in_group as "Matched Tag Group"
from d
inner join c on d.tag_group_id = c.tag_group_id
            and d.document_tag_count = c.count_tags_in_group

Una cosa interesante de este enfoque es que puede ejecutar informes como '¿Cuántos documentos tienen el 50% o más de las etiquetas en cada uno de estos grupos de etiquetas?'

select 
    d.document_id as "Document ID",
    c.tags_in_group as "Matched Tag Group"
from d
inner join c on d.tag_group_id = c.tag_group_id
            and d.document_tag_count >= 0.5 * c.count_tags_in_group
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top