Domanda

Oltre a questa domanda Query SQL che fornisce risultati distinti che corrispondono a più colonne che aveva una soluzione molto accurata, mi chiedevo come sarebbe il prossimo passo:

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

Quindi, per ottenere tutti i document_ids che hanno i tag 1 e 2 eseguiremo una query come questa:

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

Ora, ciò che sarebbe interessante sapere è come ottenere tutti i distinti document_ids che hanno i tag 1 e 2, oltre a quelli che hanno il tag 3. Potremmo immaginare di fare la stessa query ed eseguire un'unione tra di loro:

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

Ma mi chiedevo se con quella condizione aggiunta potessimo pensare a un'altra query iniziale. Sto immaginando di avere molti "sindacati" come quello con tag e conteggi tag diversi. Non sarebbe molto male in termini di prestazioni creare catene di sindacati del genere?

È stato utile?

Soluzione

Utilizza ancora una sorta di unioni, ma può essere più facile da leggere e controllare. Sono davvero interessato alla velocità di questa query su un set di dati di grandi dimensioni, quindi per favore fatemi sapere quanto è veloce. Quando ho inserito il tuo piccolo set di dati ci sono voluti 0,0001 secondi.

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

Ciò semplifica l'aggiunta di nuovi parametri poiché hai già specificato il set di risultati per ciascun tag.

Ad esempio aggiungendo:

OR dt1.document_id = t2s.document_id

fino alla fine raccoglierà anche document_id 2

Altri suggerimenti

È possibile farlo all'interno di un singolo, tuttavia è necessario promuovere la clausola WHERE nella clausola have per utilizzare un disgiuntivo.

Hai ragione, diventerà sempre più lento man mano che aggiungi nuovi tag che desideri cercare in clausole UNION aggiuntive. Ogni clausola UNION è una query aggiuntiva che deve essere pianificata ed eseguita. Inoltre non sarai in grado di ordinare quando hai finito.

Stai cercando una tecnica di archiviazione dei dati di base. Prima di tutto, lascia che ricrea lo schema con una tabella aggiuntiva.

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 tabella b contiene " gruppi di tag " ;. Il gruppo 1 include tag1 e tag2, mentre il gruppo 2 contiene tag3.

Ora puoi modificare la tabella b per rappresentare la query che ti interessa. Quando sei pronto per eseguire una query, crei tabelle temporanee per archiviare dati aggregati:

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;

Ora c contiene il numero di tag per il gruppo di tag e d contiene il numero di tag di ciascun documento per ciascun gruppo di tag. Se una riga in c corrisponde a una riga in d, significa che il documento ha tutti i tag in quel gruppo di 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

Un aspetto interessante di questo approccio è che potresti eseguire rapporti come "Quanti documenti hanno il 50% o più dei tag in ciascuno di questi gruppi di 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 >= 0.5 * c.count_tags_in_group
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top