【问题标题】:tagged dimensions in data warehouse数据仓库中的标记维度
【发布时间】:2011-04-14 16:03:40
【问题描述】:

在我的数据仓库中,我得到了一个维度,其中每条记录可以有一个或多个标签。 有没有直接的方法来为具有不同标签的维度建模?

我最初的想法是存储逗号分隔的列表,然后使用 MySQL:s FIND_IN_SET() 函数来测试标签的存在。这允许我对数据进行切片。

但是如果我希望能够按标签分组,我应该如何为我的方案建模?

示例:

两种产品:产品 A 和产品 B。A 被标记为“foo”、“bar”。 B 被标记为“bar”、“baz”。

查询:销售额,按产品标签分组。标签“bar”的组必须包含产品 A 和 B 的销售额:

foo -> sales for A
bar -> sales for B and A
baz -> sales for C

【问题讨论】:

    标签: mysql tags data-warehouse dimension


    【解决方案1】:

    例如,您可以存储标签重复的数据。

    如果您有销售额 a=10 foo + bar 和 b=20 bar + baz,您可以这样存储。

        sales_by_tag_facts
        id, tag, sale_id, amount, is_primary_record
        1   foo  A        10     true
        2   bar  A        10     false
        3   bar  B        20     true
        4   baz  B        20     false
    
    select sum(amount) from sales_by_tag_facts group by tag; // by tag
    select sum(amount) from sales_by_tag_facts where is_primary_record=true; // without tag.
    

    【讨论】:

      【解决方案2】:

      为什么不把繁重的工作从报告转移到交易预订上

      您可以添加一个名为:

      TagTotal 存储每个标签的总金额,并通过 BEFORE(AFTER)_UPDATE_EACH 触发器随每笔交易一起更新。

      额外的字段/表格

      如果您在 product 表中有 2 个额外字段:

      product.amount    decimal(10,2) running total of sales to date
      product.last_sale date          date of the last sale
      

      标签总计表如下所示。

      tag.id primary autoinc    
      tag.tagstr varchar(25)     
      tag.amount decimal(10,2)
      tag.date_from date   #dates to keep the running totals per month/week/day.
      tag.date_to   date
      

      伪代码

      CREATE TRIGGER ai_sales_each AFTER INSERT ON sales FOR EACH ROW
      BEGIN
        UPDATE product SET product.amount = product.amount + new.amount,
          product.last_sale = sale.date
      END
      
      CREATE TRIGGER au_product_each AFTER UPDATE ON product FOR EACH ROW
      BEGIN
        DECLARE AllTags VARCHAR(255);
        DECLARE ThisTag VARCHAR(25);
      
        IF old.tags <> new.tags THEN BEGIN
          reorganize the running totals in the tagtotal table.
        END; END IF;
      
        SET AllTags = new.tags;
        WHILE AllTags has more tags BEGIN
          SET ThisTag = NextTag(AllTags);
          UPDATE TagTotals SET amount = amount + new.amount
            WHERE TagTotals.tagstr = ThisTag 
            AND new.last_date BETWEEN TagTotals.date_from AND TagTotals.date_to;
        END; END WHILE; 
      

      现在,如果您想要每个标签的销售总额,您只需从 tagtotals 表中进行选择即可。
      这将立即产生结果。

      【讨论】:

        【解决方案3】:

        我建议不要这样做,它违反了规范化规则。
        I keep messing up 1NF
        或阅读normalization tag 下的帖子。

        重新设计表格的建议

        如果你像这样制作一个标签和标签链接表。

        table tag
          id autoincrement integer primary index 
          tag_str varchar(20) index unique not null
        
        table taglink
          id autoincrement integer primary index #gotta have an ID in my book.
          tag_id integer not null
          product_id integer not null
        

        你有一个类似这样的销售表。

        table product
          id autoincement integer primary index
          desc varchar(255)
          barcode, price, whatever ...
        

        选择语句来查找每个标签的产品

        然后您可以查找与标签匹配的文章,如下所示。

        select * from product
        inner join taglink on (product.id = taglink.product_id)
        inner join tag on (taglink.tag_id = tag.id)
        where tag.tag_str in ('foo','bar','baz');
        

        选择语句列出每个产品的标签

        select tag_str from tag
        inner join taglink on (taglink.tag_id = tag.id)
        inner join product on (taglink.product_id = product.id)
        where product.barcode = '4548215' or product.desc like 'OMG Po%'
        

        添加新标签

        要添加新标签,只需

        insert into tag (id, tag_str) values (
          null /*remember autoincrement*/
          ,'Mytag');
        

        链接标签

        将标签链接到产品

        set @product_id = 10;
        set @tag_id = 1;
        ...or...
        select @product_id:= product.id from product where product.barcode = '1254851';
        ...
        insert into taglink (id, product_id, tag_id) values (
          null /*autoinc id*/
          ,@product_id
          ,@tag_id );
        

        您可以将无限数量的标签链接到产品,并且不会因昂贵的FIND_IN_SET 语句而减慢查询速度。
        并且您可以防止重复的标签。
        而且您的数据库会更快更小。

        【讨论】:

        • 如何连接 3 个表,每个表有数百万行?遵守规则不是目标。非规范化是数据仓库中的常用方法。
        • 如果将 id 字段设置为哈希类型索引,则匹配将在 O(1) 时间内发生。而且所有匹配都是等连接,所以这会比你想象的要快。
        • 我建议设置一个测试台并为此计时。请让我们知道结果,我真的很好奇判决是什么。
        • 标签表很小(ish),其他2个可能很大。我有一个偷偷摸摸的怀疑内部连接的顺序是关键的速度明智。
        • 匹配速度有多快并不重要。 Mysql 无法进行合并连接或哈希连接。它必须执行嵌套循环。大约需要 1000 万*2000 万*1000*O(1) 时间。
        猜你喜欢
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多