ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【工作杂谈】20260806_CROSS JOIN 的神奇用法:灵活生成多维聚合组合

【工作杂谈】20260806_CROSS JOIN 的神奇用法:灵活生成多维聚合组合 文章目录1、CROSS JOIN简介2、大数据下的应用场景2.1 引入分组聚合函数2.1.1 大数据领域 OLAP 分组扩展引入时间线2.2 引入CROSS JOIN搭配辅助表2.3 详细步骤2.3.1 第一个组合255 0 0 0 02.3.2 第一个组合255 255 0 0 2552.3.3 组合总结3、CROSS JOIN扩展维度的优势我的网站原文 https://eleanora-lyh.github.io/MyLearningNotes/csdn处的文章会尽快同步更新欢迎大家来访问1、CROSS JOIN简介CROSS JOIN返回两表的笛卡尔积左表的每一行都会与右表的每一行组合。本例两表各有 3 行因此返回 3 \times 3 9 行NULL不参与匹配判断也不会阻止组合。连接 Hive 数据库创建示例表并填充数据createtableorders(user_id string,amountdouble)rowformat delimitedfieldsterminatedby\tstoredastextfile;insertintoordersvalues(1,100),(NULL,200),(3,300);createtableusers(user_id string,user_name string)rowformat delimitedfieldsterminatedby\tstoredastextfile;insertintousersvalues(1,Alice),(2,Bob),(NULL,Charlie);select*fromorders;select*fromusers;ordersuser_idamount1100NULL2003300usersuser_iduser_name1Alice2BobNULLCharlieSELECT*FROMorders oCROSSJOINusers u;结果user_idamountuser_iduser_name1100.01Alice1100.02Bob1100.0NULLCharlieNULL200.01AliceNULL200.02BobNULL200.0NULLCharlie3300.01Alice3300.02Bob3300.0NULLCharlieSQL 在没有ORDER BY时不保证结果顺序表格中的顺序仅用于展示。2、大数据下的应用场景2.1 引入分组聚合函数假设SourceTable表的记录如下其中的Col1, Col2,Col3,Col4, Col5对应了我们关注的5个统计维度PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5UserMUIDP1B1C1Video10AppCNSportsU1P1B1C1Video30WebCNFinaceU1P1B1C1Artical20AppCNFinaceU2P1B1C1Artical10AppCNSportsU1如果想探究Col1与用户的关系那么就需要计算在不同的Col1值下 User的数量也就是下面的sqlselectCol1,Count(distinctUserMUID)fromSourceTablegroupbyCol1同理如果想探究Col2, cols3, col4 col5与用户的关系那么就需要计算在不同的Col2, cols3, col4 col5值下 User的数量也就是下面的sqlselectCol2,Count(distinctUserMUID)fromSourceTablegroupbyCol2selectCol3,Count(distinctUserMUID)fromSourceTablegroupbyCol3selectCol4,Count(distinctUserMUID)fromSourceTablegroupbyCol4selectCol5,Count(distinctUserMUID)fromSourceTablegroupbyCol5现在问题变得更复杂如果想探究Col1, Col2的组合与用户的关系那么就需要写重新sql进行计算回想一下数学种的统计知识已知Col1, Col2, cols3, col4 col5这5个维度是互不干扰的且每个维度的值都不为空那么其所有的组合就是对5个位置的独立选择这5个空位分别有两个值可选存在/不存在。最后会得到2的5次方32共32种组合那么如果我们想全面地分析维度与用户数量的关系就应该得到32个类似上面的sql。很显然这种做法很蠢是白白浪费代码和时间属于是在时间和空间上都不讨巧的笨蛋做法。所以早在1996年就有人提出了更加优秀的解决办法分组聚合函数使用分组聚合函数ROLLUP / CUBE / GROUPING SETS可以用一个sql直接生成上面32种维度的聚合结果指定维度的组合2.1.1 大数据领域 OLAP 分组扩展引入时间线时间点事件1996Gray 等学者在论文中提出 CUBE 概念1999–2002SQL:1999 标准正式纳入 ROLLUP / CUBE / GROUPING SETS~2012–2013​Hive 0.10.0 引入 GROUPING SETS、CUBE、ROLLUP(Hadoop 生态最早原生支持​)2015 年中​Spark 1.4.0 在 DataFrame API 中引入 CUBE 算子​2015 年底​Spark 1.6 补齐 rollup()DataFrame API 多维聚合能力完整​2016PostgreSQL 9.5 才支持 CUBE / ROLLUP / GROUPING SETS2017Hive 2.3.0 对齐标准 SQL 的 GROUPING 语义由于这篇文章不是为了介绍分组聚合函数的所以对三个关键字不熟悉或者感兴趣的同学可以看我这篇文章HIVE高级分组聚合的 GROUPING SETS / ROLL UP / CUBE 关键字2.2 引入CROSS JOIN搭配辅助表当想一次SQL跑出多种维度组合的聚合结果时很容易想到 HIVE高级分组聚合的 GROUPING SETS / ROLL UP / CUBE 关键字但是有些情况还不够灵活如果5个维度之间不存在递进关系就不能使用ROLL UP5个维度完全组合会得到2^5次方32共32种维度组合但如果某些组合不想要了就不能使用CUBECUBE是自动按照维度计算全组合的假设只需其中30种的维度组合全部都在GROUPING SETS中一个个声明也很麻烦。而且如果维度从5个变为6个那么代码又要重新修改。此时如果将维度组合显示记录在一个辅助表DimensionCombinations就可以避免上面的问题从数学的角度看这5个维度的组合就是5个可重复的独立选择这5个空位分别有两个值可选不聚合/聚合分别可以抽象成0/2550是一个“保留原始维度值”的控制标记。255是一个“将该维度替换为 All”的控制标记。那么事先将这些组合写入0/255的辅助表中再和业务表执行CROSS JOIN就自然可以得出所有维度的组合。当想去掉某些维度的聚合时只需要将列值置为0则不会进入聚合阶段。这里的做法我觉得很类似用空间换时间的算法我们提前将表进行膨胀组合就省去了后面多次按照不同维度的聚合。下面以完整的32个组合的辅助表DimensionCombinations为例讲下具体怎么使用维度1维度2维度3维度4维度5000000000255000255000255000255000……………255255255255255当一条记录如下其中的Col1, Col2,Col3,Col4, Col5对应了我们关注的5个数据维度分别对应辅助表DimensionCombinations会进行组合的5个维度PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5UserMUIDP1B1C1Video10AppCNSportsU1这条记录和 辅助表的32 行 做CROSS JOIN后这一行会在逻辑上扩展成 32 行。以Col1被置为255为例讲一下此类型的组合后续会发生什么其他组合同理。当某条DimensionCombinations记录的Col1 255时该组合下所有原始Col1都被映射为统一的255虽然Col1仍出现在分组列中但由于其值完全相同效果等同于消除Col1维度。可以理解为此组合下时分组条件从Col1, Col2,Col3,Col4, Col5的5列变为了Col2,Col3,Col4,Col5的4列ExtendedResultSELECTL.PartnerID,L.BrandID,R.Col10? L.Col1 :255ASCol1,R.Col20? L.Col2 :255ASCol2,R.Col30? L.Col3 :255ASCol3,R.Col40? L.Col4 :255ASCol4,R.Col50? L.Col5 :255ASCol5,UserMUIDFROMSourceTableASLCROSSJOINDimensionCombinationsASR;那么此时再执行COUNT(DISTINCT UserMUID)如下膨胀出来的32行的具有相同的UserMUIDCROSS JOIN只是提前将所有统计维度提前应用到原始行上组合结果中如果Col1 255则表示消除了此维度只留下一个值255中文的含义就是以Col1all维度的汇总数据AggResultSELECTPartnerID,BrandID,Col1,Col2,Col3,Col4,Col5,COUNT(DISTINCTUserMUID)ASAUCountFROMExtendedResult;以此类推这样通过一个辅助表DimensionCombinations就可以通过一次分组计算得到任意5个维度的所有组合而不必指定具体维度名字。如果有其他表的其他列也需要进行5个维度的全组合也可以使用同样的辅助表。以这种方式来计算多维度下的统计数据可以减少计算资源的浪费因为只读取了一次源数据就得到了所有维度的统计结果避免了重复读取。2.3 详细步骤如果上面的使用方法的抽象概念没有看懂可以看下这里的分步详解如果上面的讲解能够理解这一小节可以跳过。表还是SourceTable以下面的记录为例PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5UserMUIDP1B1C1Video10AppCNSportsU1P1B1C1Video30WebCNFinaceU1P1B1C1Artical20AppCNFinaceU2P1B1C1Artical10AppCNSportsU1为了便于理解暂时不看DimensionCombinations的全部 32 行只取下面两个组合Col1 Col2 Col3 Col4 Col5 255 0 0 0 0 255 255 0 0 255它们分别表示255 0 0 0 0 Col1 上卷为 All其他四个维度保留原值 255 255 0 0 255 Col1、Col2、Col5 上卷为 All其他两个维度保留原值代码中的表达式R.Col10? L.Col1 :255ASCol1含义是R.Col10输出 L.Col1保留原值 R.Col1255输出255表示AllCol1Types原始4条数据经过下面的sql转换后ExtendedResultSELECTL.PartnerID,L.BrandID,L.ContentID,R.Col10? L.Col1 :255ASCol1,R.Col20? L.Col2 :255ASCol2,R.Col30? L.Col3 :255ASCol3,R.Col40? L.Col4 :255ASCol4,R.Col50? L.Col5 :255ASCol5,UserMUIDFROMSourceTableASLCROSSJOINDimensionCombinationsASR;就会膨胀得到4*32行表示原始行与32个维度的组合2.3.1 第一个组合255 0 0 0 0当组合为255 0 0 0 0表示Col1 上卷为 All。执行下面的代码后ExtendedResultSELECTL.PartnerID,L.BrandID,L.ContentID,R.Col10? L.Col1 :255ASCol1,R.Col20? L.Col2 :255ASCol2,R.Col30? L.Col3 :255ASCol3,R.Col40? L.Col4 :255ASCol4,R.Col50? L.Col5 :255ASCol5,UserMUIDFROMSourceTableASLCROSSJOINDimensionCombinationsASR;表达式会把每条记录的 Col1 都改成 255此时Col1all即不再区分 Col1第一行、第四行的数据在Col1~Col5这几列是完全一致的PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5UserMUIDP1B1C125510AppCNSportsU1P1B1C125530WebCNFinaceU1P1B1C125520AppCNFinaceU2P1B1C125510AppCNSportsU1这部分数据再进行分组AggResultSELECTPartnerID,BrandID,ContentID,Col1,Col2,Col3,Col4,Col5,COUNT(DISTINCTUserMUID)ASAUCountFROMExtendedResult;统计结果如下PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5AUCountP1B1C125510AppCNSports1P1B1C125530WebCNFinace1P1B1C125520AppCNFinace1需要注意由于原来的第一行和第四行是同一个用户所以ActiveUser通过DISTINCT只能算作一个2.3.2 第一个组合255 255 0 0 255当组合为255 255 0 0 255表示Col1,Col2,Col5 上卷为 All。执行下面的代码后ExtendedResultSELECTL.PartnerID,L.BrandID,L.ContentID,R.Col10? L.Col1 :255ASCol1,R.Col20? L.Col2 :255ASCol2,R.Col30? L.Col3 :255ASCol3,R.Col40? L.Col4 :255ASCol4,R.Col50? L.Col5 :255ASCol5,UserMUIDFROMSourceTableASLCROSSJOINDimensionCombinationsASR;表达式会把每条记录的 Col1,Col2,Col5 的值都改成 255此时Col1all, Col2all, Col5all,即不再区分 Col1,Col2,Col5第一行、第三行、第四行的数据在Col1~Col5这几列是完全一致的PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5UserMUIDP1B1C1255255AppCN255U1P1B1C1255255WebCN255U1P1B1C1255255AppCN255U2P1B1C1255255AppCN255U1这部分数据再进行分组AggResultSELECTPartnerID,BrandID,ContentID,Col1,Col2,Col3,Col4,Col5,COUNT(DISTINCTUserMUID)ASAUCountFROMExtendedResult;统计结果如下PartnerIDBrandIDContentIDCol1Col2Col3Col4Col5AUCountP1B1C1255255AppCN2552P1B1C1255255WebCN25512.3.3 组合总结通过上面的两种组合的案例应该可以很好地将这个逻辑扩展到剩余的组合中。当初始表的维度为5个时光统计一个AUCountActiveUserCount指标我们就可以膨胀出2^532倍的记录。也就是说随着表的维度增加统计的指标增加最终膨胀出的行数是以指数级别扩张的。当数据量达到千万以上时需要注意数据倾斜的问题所以这时候使用CROSS JOIN辅助表的优势就会越来越明显因为一次CROSS JOIN可以得出多个维度组合的统计不仅比分开写多条GROUP BY的代码更简洁还减少对原始表的重复扫描并减少了Reduce的次数。3、CROSS JOIN扩展维度的优势最后再总结一下使用CORSS JOIN 辅助表的好处不依赖特定CUBE语法可通过修改资源文件增加或删除某些组合。可以统一用255表示All避免用NULL与真实空值混淆。其他表的处理可以复用相近的维度组合逻辑。
返回列表