Pergunta

Além desta questão SQL consulta que dá resultados distintos que correspondem várias colunas que tinha uma solução muito elegante, eu queria saber como o próximo passo seria:

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

Assim, para obter todos os document_ids que têm tag 1 e 2 que iria realizar uma consulta como esta:

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

Agora, o que seria interessante saber é como iríamos obter todas as document_ids distintas que têm marcas 1 e 2, e, além de que os ids que têm tag 3. Poderíamos imaginar fazendo a mesma consulta e realizando uma união entre eles:

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

Mas eu estava querendo saber se com essa condição acrescentou, poderíamos pensar em outra consulta inicial. Eu estou imaginando ter muitas "uniões" como que com diferentes tags e contagens de tag. Não seria muito ruim em termos de desempenho para criar cadeias de sindicatos assim?

Foi útil?

Solução

Este ainda usa sindicatos de tipos, mas pode ser mais fácil de ler e controle. Eu estou realmente interessado na velocidade desta consulta em um grande conjunto de dados, então por favor deixe-me saber o quão rápido ele é. Quando eu coloquei em seus pequenos conjunto de dados levou 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

Isto tornará mais fácil adicionar novos parâmetros porque você já tenha especificado o conjunto de resultados para cada tag.

Por exemplo acrescentando:

OR dt1.document_id = t2s.document_id

até ao fim, também vai pegar document_id 2

Outras dicas

É possível fazer isso dentro de um único, no entanto, você vai precisar para promover o seu cláusula WHERE na cláusula having, a fim de usar uma disjuntiva.

Você está correto, que vai ficar mais lento e mais lento como você adicionar novas tags que você deseja procurar em cláusulas UNION adicionais. Cada cláusula UNION é uma consulta adicional que precisa ser planejado e executado. Além disso, você não será capaz de resolver quando você está feito.

Você está procurando uma técnica básica de armazenamento de dados. Primeiro, deixe-me recriar o esquema com uma tabela 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');

Table b contém "grupos tag". Grupo 1 inclui tag1 e tag2, enquanto o grupo 2 contém tag3.

Agora você pode modificar a tabela b para representar a consulta que você está interessado em Quando você está pronto para consulta, você cria tabelas temporárias para armazenar dados 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;

Agora c contém o número de tags para grupo tag, e d contém o número de tags cada documento tem para cada grupo de tag. Se uma linha no c corresponde a uma linha na d, então isso significa que esse documento tem todas as tags desse grupo tag.

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

Um fresco coisa sobre esta abordagem é que você pode executar relatórios como "Quantos documentos têm 50% ou mais das marcas em cada um desses grupos de tags?

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top