[知识体系] 理解 IF 和 SWITCH

  [复制链接]
查看106111 | 回复118 | 2021-2-21 19:01:52 | 显示全部楼层 |阅读模式
基础

IF 是最常见的逻辑函数,用于条件判断,它的语法和 Excel 相同。SWITCH 是多层 IF 的简化形式,本质是 IF 嵌套,但是写法更优雅,以下是两个示例:
  1. -------- IF 形式 ------------
  2. SizeDesc =
  3. IF ( Product[Size] = "S", "Small",
  4. IF ( Product[Size] = "M", "Medium",
  5. IF ( Product[Size] = "L", "Large",
  6. IF ( Product[Size] = "XL", "Extra Large", "Other" ) ) ) )

  7. -------- SWITCH 等价形式 ----------
  8. SizeDesc =
  9. SWITCH (
  10.     Product[Size],
  11.     "S", "Small",
  12.     "M", "Medium",
  13.     "L", "Large",
  14.     "XL", "Extra Large",
  15.     "Other"
  16. )
复制代码

除了用第一参数返回的值作为判断条件,SWITCH 还有一种实用的写法,适合用在逻辑判断中,在逻辑函数一文中介绍过,
  1. SWITCH (
  2.     TRUE (),
  3.     Product[Size] = "XL"
  4.         && Product[Color] = "Red", "Red and XL",
  5.     Product[Size] = "XL"
  6.         && Product[Color] = "Blue", "Blue and XL",
  7.     Product[Size] = "L"
  8.         && Product[Color] = "Green", "Green and L"
  9. )
复制代码

使用 TRUE 作为第一参数表示:“返回条件求值为 TRUE 的第一个结果”。

注意

IF 函数看似结构简单,但隐藏了一些容易被忽视的细节。

变体类型

变体数据类型用于可能返回不同数据类型的度量值,主要与 IF 配合使用,变体类型取决于度量值使用的条件表达式。例如,下面的语句可以返回整数或字符串,所以它返回一个变体类型:
  1. IF ( [measure] > 0, 1, "N/A" )
复制代码

变体只适用于度量值,不能用于计算列,因为计算列必须具有统一的数据类型


7108211936391.png

计算列的必须使用统一的数据类型

标量函数

IF 和 SWITCH 都是标量函数,所以它们只能输出标量值,不能输出表,你不能在 IF 的条件中直接返回表表达式,所以下面的写法是错误的:
  1. VAR Granularity =
  2.     IF (
  3.         SELECTEDVALUE ( 'Dynamic Axis'[Granularity] ) = "Monthly",
  4.         DATESMTD ( 'Calendar'[date] ),
  5.         DATESYTD ( 'Calendar'[date] )
  6.     )
  7. RETURN
  8.     CALCULATE (
  9.         SUM ( [SomeField] ),
  10.         Granularity
  11.     )
复制代码
逻辑表达式的一种特殊情况

当 IF 的返回条件中出现一个空值和一个逻辑表达式(即 TRUE() 或 FALSE() 时),整个公式返回逻辑表达式,忽略空值。
  1. IF ( 1 = 1, BLANK(), TRUE() )     返回 FALSE
复制代码
使用变量优化 IF 和 SWITCH

在 DAX 表达式中使用变量替换相同度量值的多次引用可以提高公式可读性、优化公式的效率,因为正确地使用变量可以防止对同一表达式多次计算。然而,在某些情况下,变量的使用也可能适得其反,对性能产生负面影响。


首先,让我们演示变量如何优化公式的执行。考虑下面的报告,Sales YTDOY 计算的是今年的累计销售额与去年累计销售额的差值,仅考虑两年中都有销售额的月份



7108211936392.png

  1. Sales YTDOY :=
  2. CALCULATE (
  3.     SUMX (
  4.         VALUES ( 'Date'[Calendar Year Month Number] ),
  5.         VAR CurrentSales = [Sales Amount]
  6.         VAR PreviousSales = [Sales LY]
  7.         RETURN
  8.             IF (
  9.                 AND (
  10.                     CurrentSales <> 0,
  11.                     PreviousSales <> 0
  12.                 ),
  13.                 CurrentSales - PreviousSales
  14.             )
  15.     ),
  16.     DATESYTD ( 'Date'[Date] )
  17. )
复制代码

Sales YTDOY 公式对于 SUMX 第一参数中的月份,每月只计算一次[Sales Amount]和[Sales LY],在后续的 IF 条件中重复调用。对比下面的公式,你会发现 Sales YTDOY 的执行效率高的多。
  1. Sales YTDOY slow :=
  2. CALCULATE (
  3.     SUMX (
  4.         VALUES ( 'Date'[Calendar Year Month Number] ),
  5.         IF (
  6.             AND (
  7.                 [Sales Amount] <> 0,
  8.                 [Sales LY] <> 0
  9.             ),
  10.             [Sales Amount] - [Sales LY]
  11.         )
  12.     ),
  13.     DATESYTD ( 'Date'[Date] )
  14. )
复制代码

尽管 DAX 引擎可能会在相同的筛选上下文([Sales Amount]和[Sales LY])中复用相同度量获得的结果,但这种理想情况并非始终发生。在这种情况下,变量是确保更好的优化代码执行的好方法。
这种理想情况称为短路计算,随着 DAX 引擎的更新,对于一些并不复杂的 IF 判断,引擎可以自动执行短路计算,以提高效率。但是对于复杂的表达式,你不能依赖引擎的判断,使用变量是最佳方法。

需要注意的是,变量的计算受定义环境的影响。例如,如果变量定义在条件判断之前,则无论条件是否命中,都将计算该变量报告中存在独立的切片器时,这将对性能产生很大影响。要理解一点,参考下面这个报告,右侧时段选择表定义了切片器,该切片器控制矩阵的可见列,矩阵使用 Sales 度量值。



7108211936393.png



sales 度量值有两种定义方式,请对比两者的不同
  1. Sales vSlow =
  2. VAR PeriodSelection =
  3.     SELECTEDVALUE ( 'Time Selection'[PeriodSelector] )
  4. VAR SalesAmount = [Sales Amount]
  5. VAR SalesLY = [Sales LY]
  6. VAR SalesYOY = [Sales YOY]
  7. VAR SalesYTD = [Sales YTD]
  8. VAR SalesLYTD = [Sales LYTD]
  9. VAR SalesYTDOY = [Sales YTDOY]
  10. RETURN
  11.     SWITCH (
  12.         PeriodSelection,
  13.         "C", SalesAmount,
  14.         "LY", SalesLY,
  15.         "YOY", SalesYOY,
  16.         "YTD", SalesYTD,
  17.         "LYTD", SalesLYTD,
  18.         "YTDOY", SalesYTDOY,
  19.         BLANK ()
  20.     )
复制代码
  1. Sales vFast =
  2. VAR PeriodSelection =
  3.     SELECTEDVALUE ( 'Time Selection'[PeriodSelector] )
  4. RETURN
  5.     SWITCH (
  6.         PeriodSelection,
  7.         "C", [Sales Amount],
  8.         "LY", [Sales LY],
  9.         "YOY", [Sales YOY],
  10.         "YTD", [Sales YTD],
  11.         "LYTD", [Sales LYTD],
  12.         "YTDOY", [Sales YTDOY],
  13.         BLANK ()
  14.     )
复制代码

考虑到每个变量只使用一次,vSlow 其实是一种冗余写法。不过,在实际使用中我们会使用这种形式的度量值来避免在模型中定义大量单独的度量值,使用 vFast 写法,可以减少公式代码量,并且获得更好的性能。

短路计算

在这种多条件计算中,我们希望公式在命中指定的分支时才计算对应的变量。在 IF 和 SWITCH 函数的计值过程中,这种优化策略称为短路计算,做法是将特定筛选器应用于 IF 或 SWITCH 的每个求值分支的筛选上下文。由于 Sales vSlow 度量值中的变量是在 IF 和 SWITCH 之前定义的,因此分支中的筛选上下文无法影响变量的计算。所有变量(和所有度量值)的计算将不考虑示例报表的 Period 切片器中所做的选择。

变量使用规范



在特定场景中,此特性可能对 DAX 度量值的性能产生严重影响,因此,使用变量最好遵循以下规则:



  • 当在同一筛选上下文中多次计算同一 DAX 表达式时,请将其定义为变量并在后续计算时引用该变量,而不是引用 DAX 表达式。
  • 当在 IF 或 SWITCH 的分支中计算 DAX 表达式时,只要有必要,请在条件分支中将表达式定义为变量-这将保持短路优化。
  • 如果仅在条件分支中使用变量,请不要在 IF 和 SWITCH 语句之外分配变量。
  • IF 和 SWITCH 的第一个参数可以使用在 IF 和 SWITCH 之前定义的变量,这不会影响性能。


关于第三条中,你可以用下面的公式理解
  1. IF (
  2.     <condition>,
  3.     -- 条件分支 1
  4.     VAR variablename = <expression>
  5.     RETURN <another expression="">,
  6.     -- 条件分支 2
  7.     VAR variablename2 = <expression2>
  8.     RETURN <another expression2="">,

  9. )
复制代码




7108211936394.zip (1.16 MB, 下载次数: 0)
回复

使用道具 举报

zhouqh | 2021-4-24 15:31:55 | 显示全部楼层
看起来好像不错的样子
回复

使用道具 举报

boynow | 2021-6-13 17:00:07 | 显示全部楼层
加油站加油
回复

使用道具 举报

zr345 | 2021-8-21 13:32:11 来自手机 | 显示全部楼层
确实不错,顶先
回复

使用道具 举报

流星雨 | 2021-9-20 15:32:34 来自手机 | 显示全部楼层
支持你哈...................................
回复

使用道具 举报

cool | 2021-9-25 22:48:42 | 显示全部楼层
佩服佩服!
回复

使用道具 举报

dgcwd | 2021-10-13 12:41:10 | 显示全部楼层
前排顶,很好!
回复

使用道具 举报

台风 | 2021-10-25 14:05:16 | 显示全部楼层
我也顶起出售广告位
回复

使用道具 举报

pipi | 2021-10-28 18:10:30 | 显示全部楼层
回个帖子,下班咯~
回复

使用道具 举报

斌仔 | 2021-11-13 07:21:44 | 显示全部楼层
楼主呀,,,您太有才了。。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则