太原网站建设托管,视频网站seo实战,网站是怎么建立起来的,搜索引擎广告的优缺点1. 理解DAX的计算上下文#xff1a;动态报表的引擎 很多刚开始用Power BI的朋友#xff0c;都会觉得DAX的度量值很神奇。你拖一个“销售额”的度量值到矩阵里#xff0c;选择不同的年份、不同的产品类别#xff0c;它显示的数字就跟着变。这背后的“魔法师”#xff0c;就…1. 理解DAX的计算上下文动态报表的引擎很多刚开始用Power BI的朋友都会觉得DAX的度量值很神奇。你拖一个“销售额”的度量值到矩阵里选择不同的年份、不同的产品类别它显示的数字就跟着变。这背后的“魔法师”就是计算上下文。你可以把它想象成报表画布上一个无形的“滤镜”。当你把“销售额”这个度量值放进一个单元格时这个单元格所在的行标题、列标题以及你从切片器里选中的选项共同构成了一个滤镜组合这个组合就是筛选上下文。DAX引擎会拿着这个滤镜去你的数据模型里“照”一下只让符合条件的数据参与计算最后把结果呈现在这个单元格里。这解决了我们做报表时最核心的一个需求动态交互。你不用为每年的数据单独写一个公式也不用为每个产品类别复制一份报表。一个定义好的度量值比如总销售额 SUM(‘销售表’[销售额])就能适应千变万化的筛选条件。这就是筛选上下文的威力——它让静态的数字变成了会“思考”、能“响应”的智能指标。但这里有个新手特别容易踩的坑。我们来看一个例子。假设你想在“产品表”里加一个计算列叫“本产品销售额占比”直觉上可能会这么写产品表[销售额占比] DIVIDE(SUM(‘销售表’[销售额]), CALCULATE(SUM(‘销售表’[销售额]), ALL(‘产品表’)))这个公式的本意是分子是当前产品对应的销售额分母是所有产品的总销售额。但你会发现这个计算列里每一行得出的百分比都是一样的都是100%为什么因为计算列的计算发生在数据刷新时它只有行上下文知道当前是哪一行产品但没有来自报表的筛选上下文。公式里的SUM(‘销售表’[销售额])在没有其他筛选的情况下直接计算的就是销售表的总和而不是当前产品对应的销售额。所以每一行的分子分母都相等结果自然是100%。正确的做法是使用度量值或者利用CALCULATE函数在计算列中“模拟”一个筛选上下文。比如在计算列里可以这样写产品表[销售额占比] DIVIDE(CALCULATE(SUM(‘销售表’[销售额])), CALCULATE(SUM(‘销售表’[销售额]), ALL(‘产品表’)))这里外层的CALCULATE把当前行的产品信息行上下文转换成了对销售表的筛选从而得到了该产品的销售额。这个例子生动地说明了行上下文让你知道“当前是谁”而筛选上下文决定了“能看到谁的数据”。理解这两者的区别和协作方式是摆脱DAX新手村的关键一步。2. 掌握核心表函数FILTER与ALL家族理解了上下文我们就有了操控数据的基础。接下来我们需要更强大的工具来主动塑造我们需要的“数据视图”这就是表函数。它们不直接返回值而是返回一张“表”。这张表可以作为其他函数的输入让我们能进行更灵活的计算。其中最常用、也最核心的两个系列就是FILTER和ALL家族。2.1 用FILTER进行精准的数据切片FILTER函数就像一把精密的手术刀它返回原表中所有满足条件的行。它的语法很简单FILTER(表, 条件)。我经常用它来构建动态的KPI关键绩效指标。比如老板想看“高价值客户”单笔订单金额大于1万元的销售贡献占比。你可以这样写一个度量值高价值客户销售额 CALCULATE( [总销售额], FILTER( ‘客户表’, [客户单笔最大金额] 10000 ) ) 高价值客户占比 DIVIDE([高价值客户销售额], [总销售额])这里FILTER创建了一个只包含高价值客户的虚拟表然后CALCULATE函数将这个表作为筛选器应用到[总销售额]的计算中。这样无论报表上如何筛选年份、地区这个占比始终反映的是高价值客户的贡献度。但使用FILTER时要注意性能。FILTER是迭代函数它会逐行扫描你给的表。如果这个表很大比如上百万行的交易明细而你的条件筛选性很强比如只留下几十行那么直接在大表上FILTER可能会比较慢。一个优化技巧是尽量先缩小要扫描的表的范围。例如如果我知道高价值客户只出现在某些城市我可以先对城市进行筛选优化版高价值客户销售额 CALCULATE( [总销售额], FILTER( // 先筛选城市减少扫描行数 CALCULATETABLE(‘客户表’, ‘客户表’[城市] IN {“北京”, “上海”, “深圳”}), [客户单笔最大金额] 10000 ) )2.2 用ALL、ALLEXCEPT和ALLSELECTED操控筛选器如果说FILTER是添加筛选器那么ALL家族函数就是用来移除或控制筛选器的。这是实现高级对比分析如占比、同环比的基石。ALL函数最简单粗暴它移除指定表或列上的所有筛选上下文。最常见的场景就是计算“总计占比”。比如在显示各产品类别销售额的矩阵里你想加一列“占比”公式可能是销售额占比 DIVIDE([销售额], CALCULATE([销售额], ALL(‘产品表’[类别])))ALL(‘产品表’[类别])移除了当前行上下文在“类别”列上的筛选让分母计算所有类别的总和从而得到每个类别占总体的比例。ALLEXCEPT函数则更精细一些。它移除表中除了你明确指定的列之外的所有筛选。假设你的报表同时按“年份”和“产品类别”切片你想计算每个类别在当年内部的占比即分母是当年所有类别的销售额而不是所有年份所有类别的销售额。你可以写当年类别占比 DIVIDE([销售额], CALCULATE([销售额], ALLEXCEPT(‘销售表’, ‘日期表’[年度])))这里ALLEXCEPT保留了“年度”上的筛选这样分母就是当前筛选年份的总销售额但移除了“产品类别”等其他所有筛选。ALLSELECTED函数是最有意思也最容易让人困惑的一个。它不理会当前视觉对象内部产生的筛选比如矩阵内部的行、列标题但尊重来自外部的筛选比如页面级筛选器、切片器或报表画布上的其他视觉对象交叉筛选。我常用它来做“父级汇总”或“基于用户选择的动态基准”。举个例子你有一个产品类别切片器用户选择了“家电”和“数码”。在矩阵中你显示这两个类别下各子类别的销售额同时想计算每个子类别在用户已选类别总和中的占比。这时分母就不能用ALL那会是所有类别的总和也不能用不加修饰的[销售额]那会是该子类别的销售额而应该用选中类别内占比 DIVIDE([销售额], CALCULATE([销售额], ALLSELECTED(‘产品表’[类别])))ALLSELECTED会忽略矩阵内部“子类别”的筛选但保留用户通过切片器对“类别”的筛选从而计算出正确的分母。3. 构建高级分析视图SUMMARIZE与数据沿袭当我们不满足于简单的聚合计算想要构建更复杂的分析视图比如自定义分组、层级汇总或者生成中间计算表时SUMMARIZE函数就闪亮登场了。3.1 SUMMARIZE你的自定义分组汇总工具SUMMARIZE的基本功能类似于SQL中的GROUP BY。它按你指定的列对数据进行分组并可以添加新的聚合列。语法是SUMMARIZE(表, 分组列1, 分组列2, …, 新列名1, 表达式1, …)。一个典型的业务场景是销售经理不想只看产品还想看不同价格区间的表现。我们可以动态创建一个价格区间维度价格区间销售分析 SUMMARIZE( ‘销售表’, ‘产品表’[产品名称], “价格区间”, SWITCH( TRUE(), ‘产品表’[单价] 100, “低价”, ‘产品表’[单价] 500, “中价”, “高价” ), “销售笔数”, COUNTROWS(‘销售表’), “销售额”, SUM(‘销售表’[销售额]) )这个表达式会生成一张新表包含产品名称、自定义的价格区间、以及对应的销售笔数和销售额。你可以把这张表作为新的数据源做进一步的可视化分析。3.2 匿名表与数据沿袭理解DAX的“血脉”使用SUMMARIZE、FILTER等函数生成的表我们称之为“匿名表”或“临时表”。它们不在数据模型中物理存储只在计算过程中存在。这里涉及到一个DAX非常核心但隐晦的概念数据沿袭。数据沿袭指的是一列数据即使被复制、移动或转换到另一张表中DAX仍然记得它最初来自哪个模型的哪一列。这份“血缘关系”至关重要因为它决定了筛选上下文能否正确传递。举个例子我们用SUMMARIZE创建了一个客户年龄的唯一组合表VAR CustomersAge SUMMARIZE(‘销售表’, ‘客户表’[客户ID], ‘销售表’[客户年龄])CustomersAge是一个匿名表它有两列。虽然这两列不在任何模型表中但它们分别“继承”了‘客户表’[客户ID]和‘销售表’[客户年龄]的数据沿袭。这意味着当你在后续计算中引用‘销售表’[客户年龄]时DAX知道该去哪里找这列数据并且筛选上下文比如筛选了特定年份也能沿着这份血缘关系正确传递到‘销售表’。正因为有数据沿袭下面两种写法在SUMMARIZE创建的匿名表上下文中通常是等价的// 写法一使用原始表列引用依赖数据沿袭 AVERAGEX(CustomersAge, ‘销售表’[客户年龄]) // 写法二直接使用列名在匿名表上下文中有效 AVERAGEX(CustomersAge, [客户年龄])但要注意你不能写成AVERAGEX(CustomersAge, CustomersAge[客户年龄])因为CustomersAge作为变量名并不是一个模型表DAX无法识别这种引用方式。理解数据沿袭能让你在编写复杂DAX表达式时清楚地知道每一列数据的“来龙去脉”避免很多意想不到的错误。4. 实战进阶组合运用解决复杂业务问题掌握了这些核心概念和函数后我们就可以像搭积木一样组合它们来解决真实的、复杂的业务分析需求。我分享一个我最近在项目中用到的案例动态滚动年度累计YTD与同期对比。业务背景是管理层需要一张仪表板能查看截至到当前所选月份比如今年8月本年累计销售额、去年同期的累计销售额以及增长率。并且当用户选择不同的月份时这些指标要能动态变化。这个需求需要综合运用计算上下文、时间智能函数和表函数。我们分步来实现第一步构建核心时间智能度量值。我们先建立两个基础度量值计算本期和同期的YTD销售额。这里假设你有一个标记为“日期表”的正确日期表并与事实表建立了关系。销售额 YTD TOTALYTD([总销售额], ‘日期表’[日期]) 销售额 PY YTD CALCULATE( [销售额 YTD], SAMEPERIODLASTYEAR(‘日期表’[日期]) )TOTALYTD会根据报表上下文中的最大日期自动计算从当年第一天到该日期的累计值。SAMEPERIODLASTYEAR则将时间上下文平移至上一年。第二步处理动态的“当前月份”筛选。问题来了当用户在切片器里选择“8月”时[销售额 YTD]会正确计算1-8月的累计。但[销售额 PY YTD]也会基于去年1-8月计算。然而业务可能想看的是“截至今年8月对比去年1-8月”。这听起来一样但如果用户选择的是“2月、5月、8月”多个月份呢我们需要一个逻辑始终获取所选日期中最大的那个即最新的月份作为YTD计算的截止点。这里就需要用到ALLSELECTED和MAXX这类表函数来动态确定上下文动态截止日期 MAX(‘日期表’[日期]) // 这个受当前筛选影响可能不是我们想要的 动态YTD截止日期 CALCULATE( MAX(‘日期表’[日期]), ALLSELECTED(‘日期表’[日期]) // 移除其他列筛选保留用户选择的日期范围 )ALLSELECTED(‘日期表’[日期])确保了即使用户在矩阵里看了某个月的数据我们获取的仍然是用户在日期切片器里选择的整个范围的最大日期。第三步构建动态的YTD计算。我们不能直接使用TOTALYTD了因为它内置的逻辑是基于上下文中的最大日期。我们需要用DATESYTD和CALCULATE手动构建动态销售额 YTD VAR CurrentSelectionEndDate [动态YTD截止日期] VAR YTDDates DATESYTD(CurrentSelectionEndDate) RETURN CALCULATE( [总销售额], YTDDates ) 动态销售额 PY YTD VAR CurrentSelectionEndDate [动态YTD截止日期] VAR PYEndDate DATE(YEAR(CurrentSelectionEndDate)-1, MONTH(CurrentSelectionEndDate), DAY(CurrentSelectionEndDate)) VAR PYYTDDates DATESYTD(PYEndDate) RETURN CALCULATE( [总销售额], PYYTDDates )第四步计算增长率并格式化。最后计算增长率就水到渠成了动态YTD增长率 DIVIDE( [动态销售额 YTD] - [动态销售额 PY YTD], [动态销售额 PY YTD] )把这个度量值的格式设置为百分比拖入卡片图或表格一个能响应用户任意月份选择、自动计算滚动年度累计及对比的智能指标就完成了。这个案例融合了上下文理解ALLSELECTED的作用、时间智能DATESYTD和变量VAR的使用是DAX进阶路上一个很好的综合练习。