【问题标题】:MySQL query over many-to-many realtion: unions?MySQL查询多对多关系:联合?
【发布时间】: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

但我想知道如果添加了该条件,我们是否可以考虑另一个初始查询。我想象有许多像这样具有不同标签和标签计数的“联合”。 创建这样的工会链在性能方面不是很糟糕吗?

【问题讨论】:

    标签: performance mysql many-to-many


    【解决方案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
        

        【讨论】:

          猜你喜欢
          • 2012-10-07
          • 2019-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-04
          • 1970-01-01
          相关资源
          最近更新 更多