MySQL查询多对多关系:联合?
Posted
技术标签:
【中文标题】MySQL查询多对多关系:联合?【英文标题】:MySQL query over many-to-many realtion: unions? 【发布时间】:2009-07-30 09:36:11 【问题描述】:除了这个问题SQL query that gives distinct results that match multiple columns 这有非常巧妙的解决方案,我想知道下一步会如何:
DOCUMENT_ID | TAG
----------------------------
1 | tag1
1 | tag2
1 | tag3
2 | tag2
3 | tag1
3 | tag2
4 | tag1
5 | tag3
因此,要获取所有具有标签 1 和 2 的 document_id,我们将执行如下查询:
SELECT document_id
FROM table
WHERE tag = 'tag1' OR tag = 'tag2'
GROUP BY document_id
HAVING COUNT(DISTINCT tag) = 2
现在,有趣的是,我们将如何获得所有具有标签 1 和 2 的不同 document_id,以及除此之外的具有标签 3 的 id。 我们可以想象进行相同的查询并在它们之间执行联合:
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
但我想知道如果添加了该条件,我们是否可以考虑另一个初始查询。我想象有许多像这样具有不同标签和标签计数的“联合”。 创建这样的工会链在性能方面不是很糟糕吗?
【问题讨论】:
【参考方案1】:这仍然使用各种联合,但可能更易于阅读和控制。我对这个查询在大型数据集上的速度非常感兴趣,所以请告诉我它有多快。当我放入你的小数据集时,它花了 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
【讨论】:
【参考方案2】:可以在单个子句中执行此操作,但是您需要将 WHERE 子句提升为 have 子句才能使用析取词。
【讨论】:
【参考方案3】:您是对的,当您添加要在其他 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
【讨论】:
以上是关于MySQL查询多对多关系:联合?的主要内容,如果未能解决你的问题,请参考以下文章
Mysql连接查询匹配所有标签的多个“标签”(多对多关系)?