MySQL запрос по многим многим реальностям: союзы? - PullRequest
1 голос
/ 30 июля 2009

В дополнение к этому вопросу SQL-запрос, который дает отличные результаты, соответствующие нескольким столбцам у которого было очень аккуратное решение, мне было интересно, как будет выглядеть следующий шаг:

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

Итак, чтобы получить все document_ids, имеющие теги 1 и 2, мы выполнили бы запрос, подобный этому:

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

Теперь было бы интересно узнать, как мы можем получить все различные document_ids, имеющие теги 1 и 2, и в дополнение к этому идентификаторы, имеющие тег 3 Мы могли бы представить, что делаем один и тот же запрос и выполняем объединение между ними:

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

Но мне было интересно, если бы с этим условием мы могли подумать о другом начальном запросе. Я представляю, что у меня много таких «союзов» с разными тегами и количеством тегов. Разве не было бы очень плохо с точки зрения производительности создавать подобные цепочки союзов?

Ответы [ 3 ]

2 голосов
/ 30 июля 2009

Это все еще использует своего рода объединения, но может быть легче читать и контролировать. Я действительно заинтересован в скорости этого запроса для большого набора данных, поэтому, пожалуйста, дайте мне знать, насколько он быстр. Когда я вставил небольшой набор данных, это заняло 0,0001 с.

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

Это позволит легко добавлять новые параметры, поскольку вы уже указали набор результатов для каждого тега.

Например добавление:

OR dt1.document_id = t2s.document_id

до конца также заберет document_id 2

0 голосов
/ 27 февраля 2010

Вы правы, это будет становиться все медленнее и медленнее по мере добавления новых тегов, которые вы хотите искать в дополнительных предложениях UNION. Каждое предложение UNION - это дополнительный запрос, который необходимо запланировать и выполнить. Кроме того, вы не сможете сортировать, когда закончите.

Вы ищете базовую технику хранения данных. Сначала позвольте мне воссоздать вашу схему с одной дополнительной таблицей.

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');

Таблица b содержит «группы тегов». Группа 1 включает в себя tag1 и tag2, а группа 2 содержит tag3.

Теперь вы можете изменить таблицу b, чтобы представить интересующий вас запрос. Когда вы будете готовы сделать запрос, вы создадите временные таблицы для хранения сводных данных:

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;

Теперь c содержит количество тегов для группы тегов, а d содержит количество тегов, которые каждый документ имеет для каждой группы тегов. Если строка в c совпадает со строкой в ​​d, то это означает, что документ содержит все теги в этой группе тегов.

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

Отличительной особенностью этого подхода является то, что вы можете запускать отчеты типа «Сколько документов имеют 50% или более тегов в каждой из этих групп тегов?»

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
0 голосов
/ 30 июля 2009

Это можно сделать за один раз, однако вам нужно добавить предложение WHERE в предложение has для использования дизъюнкции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...