05 基础函数类型


| | | 函数 | | | | —- | —- | —- | —- | —- | | | |
|
|
| | | | |
|
| | | |
|
|
| | | 文本函数 |
|
|
| | | 聚合函数 | |
|
| | | |
|
|
| | | |
|
|
| | | |
|
|
| | | |
| | | | | 聚合函数 |
|
|
| | | | |
|
| | |
|
|
|
| | |
|
|
|
| | |
|
|
|
| | |
|
|
|
| | 时间智能函数 |
|
|
|
| | 统计类函数 |
|
|
|
| | 投影函数 |
|
|
|
| | 分组函数 |
|
|
|
| | 连接函数 |
|
|
|
| | 集合函数 |
|
|
|
| | |
|
|
|
|

| # 05.01 日期时间函数 来源网址:https://www.powerbigeek.com/datetime-functions/ | | —- |


# 了解日期时间函数 DATE ( , , )DATEVALUE ( )DAY ( )EDATE ( , )EOMONTH ( , )HOUR ( )MONTH ( )MINUTE ( )NOW ( )SECOND ( )TIME ( , , )TIMEVALUE ( )TODAY ( )WEEKDAY ( , [] )WEEKNUM ( , [] )YEAR ( )YEARFRAC ( , , [] )##

实例

DAYFORMATYEAR05 基础函数类型 - 图1

| # 05.02 关系函数 来源网址:https://www.powerbigeek.com/related-and-relatedtable/ | | —- |

为了便于在公式模型内部操作关系,DAX提供了两个非常有用的函数:RELATED和RELATEDTABLE.你已经知道,计算列可以在定义它的表中引用列值,因此,在销售表中定义的计算列可以引用所在表的任何列。但是,如果必须引用另一个表中的列,该怎么办呢?通常,除非模型中定义了两个表之间的关系,否则不能使用其他表中的列。如果两个表建立了关系,那么可以使用RELATED函数访问相关表中的列。## 从关系的多端访问一端 例如,你可能希望在销售表中定义这样一个计算列,该列检查已销售的产品是否属于“手机”类别,当满足条件时,适当缩减标准成本。要计算这样一个列,你必须使用一个条件来检查产品类别的值,该值不在销售表中。然而,模型中的关系链从销售表开始,通过产品表和产品子类别表到达产品类别表,如下图所示。05 基础函数类型 - 图2Sales表和Product Category表通过关系链接到一起从原始表到目标表之间需要经过多少步并不重要,DAX将遵循完整的关系链,并返回相关列值。因此,可调整成本(AdjustedCost)列的公式为:Sales[AdjustedCost]=IF(RELATED(‘Product Category’[Category])=”Cell Phone”,Sales[UnitCost]*0.95,Sales[UnitCost])在一对多关系中,RELATED可以从多端访问一端,因为在这种情况下,关联表中只有一行(如果有的话)。如果不存在满足条件的行,RELATED返回空。## 从关系的一端访问多端 如果你希望从关系的一端访问多端,那么RELATED就不适用了,因为另一端(关系的多端)可能存在许多行对单个行可用。在这种情况下,可以使用RELATEDTABLE。RELATEDTABLE返回一个表,其中包含与当前表相关的所有行。例如,如果你想知道每个类别中有多少个产品,你可以使用以下公式在产品类别表中创建一个列:COUNTROWS(RELATEDTABLE(Product))此计算列将显示每个产品类别的相关产品数量,如图所示。05 基础函数类型 - 图3使用RELATEDTABLE 计算每个类别的产品数量就像RELATED一样,RELATEDTABLE沿着模型关系链,总是从一端指向多端。RELATEDTETABLE函数执行从行上下文到筛选上下文的转换,并在生成的筛选上下文中计值表达式。它是CALCULATETABLE的简化版,没有其他筛选器,只接受表引用而不接受表达式。 扩展阅读:理解关系

| # 05.03 转换函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

DAX根据运算符的需要执行数据类型的自动转换。即使这种行为是自动发生的,DAX仍然提供了一组函数用来执行显示的类型转换。## CURRENCY CURRENCY ( )将表达式结果转换为货币类型

  1. CURRENCY函数四舍五入第5位有效小数的值,返回第4位小数;如果第5位有效小数等于或大于5,则进行舍入。例如,如果value是3.6666666666666,那么转换成货币返回$3.6667;但是,如果值是3.0123456789,那么转换成货币将返回3.0123.
  2. 如果表达式的数据类型是True/False,那么CURRENCY()将为真值返回$1.0000,为假返回$0.0000。
  3. 如果表达式的数据类型是文本,则CURRENCY()将尝试将文本转换为数字;如果转换成功,数字将被转换为货币,否则将返回一个错误。
  4. 如果表达式的数据类型是DateTime,那么CURRENCY()将把DateTime值转换为一个数字,然后将这个数字转换为货币。DateTime值包含一个整数部分,表示给定日期到1900-03-01之间的天数,以及一个分数,表示一天的分数(其中12小时或中午是0.5天)。如果表达式的值不是正确的DateTime值,则返回一个错误。

    INT

    INT ( )将表达式转换为整数(向下舍入)INT(-2.9)= -3INT(-2.1)= -3INT(-0.9)= -1INT(-0.1)= -1INT(0.1)=0INT(0.9)=0INT(2.1)=2INT(2.9)=2## TRUNC TRUNC ( , [] )通过删除数字的小数或小数部分将数字截断为整数在截断负数的时候,TRUNC和INT函数的行为有所不同。
  • 如果NumberOfDigits大于0,则number被截断为指定的小数位数。
  • 如果NumberOfDigits为0,则该数字将被截断为整数部分。
  • 如果NumberOfDigits小于0,则将该数字截断到小数点的左边。

    ROUND

    ROUND ( , )将数字四舍五入为指定的数字
  • 如果NumberOfDigits大于0,将数字四舍五入到指定的小数位数。
  • 如果NumberOfDigits是0,将数字四舍五入到最近的整数。
  • 如果NumberOfDigits小于0,将数字四舍五入到小数点的左边。 要更精确的控制舍入行为,可以参考向下舍入ROUNDDOWN、向上舍入ROUNDUP。## MROUND MROUND(, )返回一个四舍五入到所需倍数 的数字。若除以指定倍数后的余数大于或等于倍数的一半,则四舍五入。MROUND(1.3,0.2)=>1.4MROUND(-10,-3)=> -9MROUND(5,-2)=> error当为小数值舍入时,最终的结果的位数是不确定的。这是Excel中一个已知BUG,DAX继承了此BUG。例如MROUND(6.05,0.1)返回6.0,而MROUND(7.05,0.1)返回7.1。## 日期和时间转换 DATE和TIME将日期和时间部分作为参数并返回正确的日期时间格式。DATE ( , , )以datetime格式返回指定的日期TIME ( , , )将数字形式的小时、分钟和秒转换为datetime格式的时间## VALUE VALUE ( )将字符串转换为数字格式作为文本参数传递的text可以是DAX识别的任何常量、数字、日期或时间格式。如果不是这些格式,则返回错误。VALUE函数并不常用,因为DAX根据需要隐式地将文本转换为数字。## CONVERT CONVERT ( , )将表达式转换为指定的数据类型,可转换为整数,双精度,字符串,布尔值,货币,日期时间类型## FORMAT FORMAT ( , )将值转换为指定数字格式的文本以数值作为第一个参数,以字符串格式作为第二个参数,FORMAT将数值转换为文本字符串。通常与DateTime格式一起使用。例如,下面的表达式返回“2015年1月12日”FORMAT(DATE(2015,01,12),”yyyy mmm dd”)相反的操作(将字符串转换为DateTime值)是通过DATEVALUE函数执行的。DAX格式函数的参数所支持的格式字符串是基于Visual Basic (OLE自动化)使用的格式字符串,而不是. net框架使用的格式字符串。因此,如果参数不匹配任何定义的格式字符串,可能会得到意外的结果或错误。例如,不支持“p”作为“Percent”的缩写。作为FORMAT函数的参数提供的字符串(不包括在预定义格式字符串列表中)将作为自定义格式字符串的一部分或字符串文本处理。

    预定义的数字格式

    | 参数类型 | 参数说明(注意:返回的都是文本) | | :—-: | :—-: | | “General Number” | 显示没有千分符的数字 | | “Currency” | 显示带千分符的数字,对于符合要求的数字,在十进制分隔符的右边显示两个数字。输出的内容基于系统区域设置。 | | “Fixed” | 在十进制分隔符的左边显示至少一个数字,右边显示至少两个数字。 | | “Standard” | 显示带有千分符的数字,小数点分隔符的左边至少有一个数字,右边至少有两个数字。 | | “Percent” | 将显示的数字乘以100,并在右侧添加百分号(%);始终在十进制分隔符的右边显示两个数字。 | | “Scientific” | 使用标准科学符号,提供两个有效数字。 | | “Yes/No” | 如果数字为0,则显示No;否则显示Yes。 | | “True/False” | 如果数字为0,则显示False;否则显示True。 | | “On/Off” | 如果数字为0,则显示Off,否则显示On。 |

FORMAT(12345.67,”General Number”)=12345.67FORMAT(12345.67,”Currency”)= ¥12345.67FORMAT(12345.67,”Fixed”)=12345.67FORMAT(12345.67,”Standard”)=12,345.67FORMAT(12345.67,”Percent”)=1,234,567.00%FORMAT(12345.67,”Scientific”)=1.23E+04### 可自定义的数字格式 自定义的数字格式表达式可以有1到3个由分号分隔的部分。如果Format函数的样式参数包含预定义的数字格式之一,则只允许有一个部分。 | 使用的参数 | 返回结果 | | :—-: | :—-: | | 一个 | 应用到所有值 | | 两个 | 第一个适用于正值和零;第二个适用于负值。 | | 三个 | 第一部分适用于正值,第二部分适用于负值,第三部分适用于零。 |

=Format([Measure],”#,##.#0;(#,##.#0)”)//财务格式,负数用括号表示| Format (Style) | “5” 格式化为 | “-5” 格式化为 | “0.5” 格式化为 | “0” 格式化为 | | :—-: | :—-: | :—-: | :—-: | :—-: | | Zero-length string (“”) | 5 | -5 | 0.5 | 0 | | 0 | 5 | -5 | 1 | 0 | | 0.00 | 5.00 | -5.00 | 0.50 | 0.00 | | #,##0 | 5 | -5 | 1 | 0 | | $#,##0;($#,##0) | $5 | ($5) | $1 | $0 | | $#,##0.00;($#,##0.00) | $5.00 | ($5.00) | $0.50 | $0.00 | | 0% | 500% | -500% | 50% | 0% | | 0.00% | 500.00% | -500.00% | 50.00% | 0.00% | | 0.00E+00 | 5.00E+00 | -5.00E+00 | 5.00E-01 | 0.00E+00 | | 0.00E-00 | 5.00E00 | -5.00E00 | 5.00E-01 | 0.00E00 | | “$#,##0;;\Z\e\r\o” | $5 | $-5 | $1 | Zero |


### 预定义的日期和时间格式 下表标识了预定义的日期和时间格式名称。如果你使用的字符串不是下面的格式,它们将被解释为自定义日期和时间格式。 | 参数类型 | 参数说明(注意:返回的都是文本) | | :—-: | :—-: | | “General Date” | 显示日期和/或时间。例如,3/12/2008 11:07:31 AM。日期显示由应用程序的当前区域决定 | | “Long Date” or “Medium Date” | 根据当前区域设置的长日期格式显示日期。例如,2008年3月12日星期三 | | “Short Date” | 使用当前区域设置的短日期格式显示日期。例如,3/12/2008 | | “Long Time” or | 使用当前区域设定的长时间格式显示时间;通常包括小时、分钟、秒。例如,11:07:31 AM | | “Medium Time” | 显示12小时格式的时间。例如 11:07 AM | | “Short Time” | 显示24小时格式的时间。例如 11:07 |


### 自定义的日期和时间格式 | 参数类型 | 参数说明(注意:返回的都是文本) | | :—-: | :—-: | | (:) | Time separator. In some locales, other characters may be used to represent the time separator. The time separator separates hours, minutes, and seconds when time values are formatted. The actual character that is used as the time separator in formatted output is determined by your application’s current culture value. | | (/) | Date separator. In some locales, other characters may be used to represent the date separator. The date separator separates the day, month, and year when date values are formatted. The actual character that is used as the date separator in formatted output is determined by your application’s current culture. | | (%) | Used to indicate that the following character should be read as a single-letter format without regard to any trailing letters. Also used to indicate that a single-letter format is read as a user-defined format. See what follows for additional details. | | d | Displays the day as a number without a leading zero (for example, 1). Use %d if this is the only character in your user-defined numeric format. | | dd | Displays the day as a number with a leading zero (for example, 01). | | ddd | Displays the day as an abbreviation (for example, Sun). | | dddd | Displays the day as a full name (for example, Sunday). | | M | Displays the month as a number without a leading zero (for example, January is represented as 1). Use %M if this is the only character in your user-defined numeric format. | | MM | Displays the month as a number with a leading zero (for example, 01/12/01). | | MMM | Displays the month as an abbreviation (for example, Jan). | | MMMM | Displays the month as a full month name (for example, January). | | gg | Displays the period/era string (for example, A.D.). | | h | Displays the hour as a number without leading zeros using the 12-hour clock (for example, 1:15:15 PM). Use %h if this is the only character in your user-defined numeric format. | | hh | Displays the hour as a number with leading zeros using the 12-hour clock (for example, 01:15:15 PM). | | H | Displays the hour as a number without leading zeros using the 24-hour clock (for example, 1:15:15). Use %H if this is the only character in your user-defined numeric format. | | HH | Displays the hour as a number with leading zeros using the 24-hour clock (for example, 01:15:15). | | m | Displays the minute as a number without leading zeros (for example, 12:1:15). Use %m if this is the only character in your user-defined numeric format. | | mm | Displays the minute as a number with leading zeros (for example, 12:01:15). | | s | Displays the second as a number without leading zeros (for example, 12:15:5). Use %s if this is the only character in your user-defined numeric format. | | ss | Displays the second as a number with leading zeros (for example, 12:15:05). | | AM/PM | Use the 12-hour clock and display an uppercase AM with any hour before noon; display an uppercase PM with any hour between noon and 11:59 P.M. | | am/pm | Use the 12-hour clock and display a lowercase AM with any hour before noon; display a lowercase PM with any hour between noon and 11:59 P.M. | | A/P | Use the 12-hour clock and display an uppercase A with any hour before noon; display an uppercase P with any hour between noon and 11:59 P.M. | | a/p | Use the 12-hour clock and display a lowercase A with any hour before noon; display a lowercase P with any hour between noon and 11:59 P.M. | | AMPM | Use the 12-hour clock and display the AM string literal as defined by your system with any hour before noon; display the PM string literal as defined by your system with any hour between noon and 11:59 P.M. AMPM can be either uppercase or lowercase, but the case of the string displayed matches the string as defined by your system settings. The default format is AM/PM. | | y | Displays the year number (0-9) without leading zeros. Use %y if this is the only character in your user-defined numeric format. | | yy | Displays the year in two-digit numeric format with a leading zero, if applicable. | | yyy | Displays the year in four-digit numeric format. | | yyyy | Displays the year in four-digit numeric format. | | z | Displays the timezone offset without a leading zero (for example, -8). Use %z if this is the only character in your user-defined numeric format. | | zz | Displays the timezone offset with a leading zero (for example, -08) | | zzz | Displays the full timezone offset (for example, -08:00) |


格式化参数是区分大小写的。使用不同的大小写可以获得不同的格式。例如,当用字符串“D”格式化日期值时,你将得到长格式的日期(根据你当前的语言环境)。但是,如果将大小写改为“d”,则会得到短格式的日期。此外,如果预期的格式与任何已定义格式字符串的情况不匹配,可能会出现意外结果或错误。

| # 05.04 文本函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

了解文本函数

DAX 中几乎所有可用的文本函数都与Excel类似, 只有少数例外:它们是CONCATENATE, EXACT,FIND, FIXED,FORMAT, LEFT, LEN, LOWER, MID, REPLACE, REPT, RIGHT,SEARCH, SUBSTITUTE, TRIM, UPPER和 VALUE. 这些函数对于操作文本和从包含多个值的字符串中提取数据非常有用。## 实例 例如,下图中,你可以看到一个从字符串中提取名和姓的示例,该字符串包含用逗号分隔的值,中间是标题,我们希望将其删除。05 基础函数类型 - 图4使用文本函数提取姓名的示例我们从计算两个逗号的位置开始,然后使用代表位置的数字提取文本的目标部分。SimpleConversion列使用的公式在字符串少于两个逗号时可能会返回错误值 (如果压根没有逗号,则会引发错误),而FirstLastName列使用了一个更复杂的表达式,不会在缺少逗号的情况下失败:Comma1 =IFERROR(FIND(“,”, People[Name]),BLANK())Comma2 =IFERROR(FIND(“,”, People[Name], People[Comma1]+1),BLANK())SimpleConversion =MID(People[Name],People[Comma2]+1,LEN(People[Name]))&” “&LEFT(People[Name],People[Comma1]-1)FirstLastName =TRIM(MID(People[Name],IF(ISNUMBER(People[Comma2]),People[Comma2],People[Comma1])+1,LEN(People[Name])))&IF(ISNUMBER(People[Comma1]),” “&LEFT(People[Name],People[Comma1]-1),””)如你所见,FirstLastName列是由一个很长的DAX表达式定义的,但是你必须使用它来避免可能的错误,因为如果单个值产生错误,就会传递到整个列。虽然DAX具备提取从文本中提取数据的能力,但建议你尽量在编辑查询阶段或数据库中提前完成类似操作,避免在模型中进行数据清洗。

| # 05.05 聚合函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

认识聚合函数

几乎所有数据模型都需要对聚合数据进行操作。DAX提供了一组函数,它们可以聚合表中某一列的值,并返回一个值。我们称这组函数为聚合函数。例如,以下度量值用于计算销售表SalesAmount列中所有数字的总和:Sales:=SUM(Sales[SalesAmount])如果在计算列中使用这个表达式,它将聚合表的所有行;如果将其用在度量值中,它将综合考虑所在透视表的切片器、行、列筛选器和其他筛选条件,并在这些条件筛选后的行中计值。常用的聚合函数(SUM、AVERAGE、MIN、MAX、STDEV和VAR)只对数值或日期进行操作。聚合函数是最基础也是最常用的函数,因为度量值必须返回标量结果(数值),这意味着即使计算过程中使用表作为筛选条件,最终也必须通过聚合函数输出一个标量。

统计文本的聚合函数

与Excel类似,DAX为聚合函数提供了另一种可选语法,可以对包含数值和非数值的列进行计算,比如文本列。该语法将后缀A添加到函数的名称中,以获得与Excel相同的名称和行为。但是,这些函数只对包含真/假值的列有用,因为TRUE被计算为1,FALSE为0,而文本列始终被视为0。因此,无论列的内容是什么,如果在文本列上使用MAXA,结果总是0。此外,DAX在执行聚合时从不考虑空单元格。即使这些函数可以在不返回错误的非数字列上使用,它们的结果通常也没有用,因为文本列无法被自动转换为数值。这些函数是AVERAGEA、COUNTA、MINA和MAXA。尽管这些统计函数的名称相同,但是它们在DAX和Excel中使用的方式存在差异,因为在DAX中,列具有类型,并且其类型确定了聚合函数的行为。Excel以单元格为单位处理数据类型,而DAX以列为单位处理数据类型。DAX 以表格形式处理数据, 每列都具有规范定义的类型, 而 Excel 公式则适用于异构单元格值, 没有明确定义的类型。如果Power Pivot中的列具有数字类型,那么所有值只能是数字或空单元格。如果列是文本类型,那么这些函数(COUNTA除外)的值总是0,即使文本可以转换为数字。而在Excel中,值是按逐个单元格计算的。综上所述,这些DAX函数对于文本列不是很有用。## 特殊用法 MIN和MAX还有另一个功能:如果使用两个参数,它们将返回两个参数的最小值或最大值。因此,MIN(1,2)将返回1,MAX(1,2)将返回2。这个功能新增于2015年,在计算复杂表达式的最小值或最大值时非常有用,因为它避免了在IF语句中多次编写相同的表达式。## 计数类型 前面学习的函数对于计算聚合值非常有用。但有时候,你对聚合值不感兴趣,而只对计数感兴趣。因此,DAX提供了一组有用的函数,可用于对行或值计数:

  • COUNT仅在数字列上运行
  • COUNTA在任何类型的列上运行
  • COUNTBLANK返回列中空单元格的数量
  • COUNTROWS返回表中的行数
  • DISTINCTCOUNT返回列中非重复值的个数 COUNTA是后缀为A的函数组中一个有趣的函数,因为它返回列中非空行的数量,可以在任何类型的列上工作。如果要计算列中包含空值的数量,可以使用COUNTBLANK函数。最后,如果要计算表的行数,可以使用COUNTROWS函数。注意,COUNTROWS需要一个表作为参数,而不是列。对于任何表的任何列,COUNTA(table[column]) + COUNTBLANK(table[column])的结果总是等于COUNTROWS (table)最后一个函数DISTINCTCOUNT非常有用,因为它完全按照名字的含义运行:统计列的不同值的数量,用列作为惟一的参数。DISTINCTCOUNT将空白值视作可能的值之一。DISTINCTCOUNT是在2012版本的DAX中引入的函数。早期版本的 DAX 不包括非重复计数,若要计算列的不同值的数目,必须使用COUNTROWS ( DISTINCT ( table[column] )。这两种写法的原理相同,尽管DISTINCTCOUNT更易于阅读,只需要调用一个函数,但其本质是COUNTROWS ( DISTINCT ( table[column] )的语法糖

    聚合表达式的聚合函数

    到目前为止我们学习的所有聚合函数都是针对列的(COUNTROWS除外,它适用于表)。因此,它们只能聚合单列的值。有一些聚合函数可以聚合一个表达式,而不是单个列。当你想使用相关表的不同列进行计算时,这组函数非常有用。例如,如果销售表包含所有销售交易记录,而与销售表相关的产品表包含所有产品信息,包括成本,你可以通过使用以下表达式定义的度量值来计算销售交易的内部总成本:Cost:=SUMX(Sales, Sales[Quantity]*RELATED(Product[StandardCost]))此度量值计算销售表中每一行的销售数量(从销售表)和销售产品的标准成本(从产品表相关行)的乘积。最后,它返回所有计算结果的总和。以X后缀结尾的所有聚合函数都有这样的行为:它们在表(第一参数)的每行的执行表达式 (第二参数), 最后由相应的聚合函数(SUM, MIN, MAX和COUNT) 返回应用于表达式的结果。在理解迭代函数一文中,你将进一步了解这种行为,届时,我们将引入计值上下文的概念。X-后缀函数有SUMXAVERAGEX、PRODUCTX、COUNTX、COUNTAX、CONCATENATEX、MINX和MAXX。还有一些迭代器没有X后缀,比如FILTER和ADDCOLUMNS。稍后将详细介绍。如果按聚合的对象分类,我们可以把聚合函数划分为三种不同类型:聚合列、聚合表达式和聚合表# 深入理解聚合函数 聚合函数的行为在简单场景下非常直观,与Excel函数类似,不需要做额外说明。但是在一些特定的上下文环境中,它的行为可能有一些古怪,很多新手在刚刚接触DAX的时候会在这里遇到困难。其实,一旦理解了聚合函数的本质,并且积累了对上下文的认识,对这个问题的理解就会逐渐清晰。 上文提到过,如果在计算列中使用聚合函数,它将聚合表的所有行,这是因为聚合函数忽略行上下文,只考虑筛选上下文,所以除非发生行上下文转换,否则聚合函数始终对整列执行聚合。当你理解了聚合函数在计算列中的计值流之后,让我们加入一点点变化,下面这个度量值可能让很多DAX初学者感到匪夷所思:截至当前日期的累计销售额 =CALCULATE(SUM(SALES[Sales amount]),FILTER(ALL(Date),Date[date]<=MAX(Date[date])))我们不讨论这个公式的其他部分,只把精力集中在高亮的第七行,如果你认为这里的MAX返回的是当前行上下文的日期,那么FILTER的第二参数在每行计值时得到的结果都是True,FILTER将返回整个日期表,这显示是错误的。实际上,MAX作为一个聚合函数,在这里仍然遵循忽略行上下文,只考虑筛选上下文的规律。后面在介绍CALCULATE的时候你会了解到,FILTER表达式在原始筛选上下文条件下计值,所以MAX ( Date[date] )的筛选上下文来自外部筛选器,也就是报表筛选条件约束下的日期,MAX在这个日期范围中取最大值用于逐行判断。## 聚合函数的本质 聚合函数忽略行上下文,只考虑筛选上下文的行为似乎过于抽象,但当你了解聚合函数的本质后,这种行为就会变的非常直观。因为任何一个聚合函数都是下面这种写法的简化,比如 SUM(Table[Column]) 的本质是 SUMX(Table, Table[Column])仔细考虑一下上面的写法,对于这个公式而言,SUMX的第一参数会被来自外部的筛选上下文所影响,而行上下文不会对它产生任何作用。其他聚合函数也具有类似的等价转换,实际上聚合函数是一类特殊的Aggregation – X函数的简化形式。这也解释了本小节开始时提出的疑问。

| # 05.06 逻辑函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

我们经常需要在表达式中构建逻辑条件,例如,根据列值或截获的错误条件来实现不同的计算。在这些情况下,你可以使用 DAX 中的逻辑函数。在处理DAX表达式中的错误一文,你已经了解到这类DAX表达式的两个最重要的函数是IF和IFERROR。# 了解逻辑函数 逻辑函数非常直观,函数的名字就代表了功能,它们是AND, FALSE,IF, IFERROR, NOT, TRUE和OR。例如,你希望仅当价格列包含正确的数值时,才将数量乘以价格,则可以使用以下模式:Amount =IFERROR(Sales[Quantity]* Sales[Price],BLANK())如果你没有使用IFERROR,并且价格列包含无效的数字,那么计算列将提示错误,因为如果单个行产生了计算错误,这个错误将传递到整个列。但是,使用IFERROR会截获错误,并将其替换为空值。在处理DAX表达式中的错误一文的最后,你将了解到IFERROR是个需要谨慎使用的函数。## SWITCH 这个类别中另一个有趣的函数是SWITCH,当你有一个包含少量不重复值的列,并且希望根据列值获得不同的行为时,它非常有用。例如,产品表的尺寸列包含L、M、S、XL,你可能希望新建一列来解码这些值。你可以通过使用嵌套的IF函数获得结果:SizeDesc =IF(DProduct[Size]=”S”,”Small”,IF(Product[Size]=”M”,”Medium”,IF(Product[Size]=”L”,”Large”,IF(Product[Size]=”XL”,”Extra Large”,”Other”))))使用SWITCH可以更方便地表达同一个公式:SizeDesc =SWITCH(Product[Size],”S”,”Small”,”M”,”Medium”,”L”,”Large”,”XL”,”Extra Large”,”Other”)后一种表达式虽然性能没有变化,但代码的可读性更好,因为在引擎内部,DAX将SWITCH语句转换为一组嵌套的IF函数。## SWITCH的特殊用法 关于SWITCH函数有个有趣的用法,你可以用它来检查同一个表达式中的多个条件,因为SWITCH被转换为一组嵌套IF,其中第一个匹配到的条件胜出,你可以使用此模式测试多个条件:SWITCH(TRUE(),Product[Size]=”XL”&& Product[Color]=”Red”,”Red and XL”,Product[Size]=”XL”&& Product[Color]=”Blue”,”Blue and XL”,Product[Size]=”L”&& Product[Color]=”Green”,”Green and L”)使用TRUE作为第一参数的作用是,返回条件判断List中为第一个为TRUE的结果

| # 05.07 三角函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

了解三角函数

DAX提供了丰富的三角函数,这些函数对于某些计算非常有用。因为它们的用法比较简单,这里不再深入介绍。它们是## COS COS ( )返回给定角度的余弦值## COSH COSH ( )返回给定数字的双曲余弦值## COT COT ( )返回以弧度表示的角度的余切值## COTH COTH ( )返回双曲角度的双曲余切值## SIN SIN ( )返回给定角度的正弦值## SINH SINH ( )返回一个数字的双曲正弦值## TAN TAN ( )返回给定角度的正切值## TANH TANH ( )返回给定数字的双曲正切值以上函数增加前缀A可以用于计算弧度(相当于arcsine,arccosine等等),此处不再赘述## DEGREES DEGREES ( )将弧度转换为角度## RADIANS RADIANS ( )将角度转换为弧度## SQRT SQRT ( )返回给定数字的平方根## SQRTPI SQRTPI ( )返回(number * pi)的平方根

| # 05.08 数学函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

常见的数学函数

DAX的数学函数集与Excel的非常相似,具有相同的语法和行为。## ABS ABS ( )返回给定数字的绝对值,具有相同数据类型但没有符号的数值。## DIVIDE DIVIDE(, , [])安全除法,可以在除数为0时返回替代结果或空值(默认)。DIVIDE(5,2)=>2.5DIVIDE(5,0)=> 空值DIVIDE(5,0,1)=>1DIVIDE检查分母是否为零比IF语句更快,但是,DIVIDE在公式引擎中执行,它的速度不如直接相除来的快,关于DIVIDE性能的分析,可以参考这篇文章## EXP EXP ( )返回自然常数e的给定数字次方## FACT FACT ( )返回一个数字的阶乘,等于123 ## LN LN ( )返回一个数字的自然对数## LOG LOG ( , [] )返回以指定数字为底的对数,省略Base以10为底数## LOG10 LOG10 ( )返回以10为底的对数## MOD MOD ( , )返回指定数字被整除后的余数MOD(0,2)— returns0MOD(5,2)— returns1MOD(6,3)— returns0MOD(7,3)— returns1MOD(8,3)— returns2## PI PI ( )返回圆周率3.14159265358979的值,精确到15位## POWER POWER ( , )返回提升到幂的数字的结果5^4=POWER(5,4)## SIGN SIGN ( )返回数字的符号:如果数字是正数,则返回1;如果数字是零,则返回0;如果数字是负数,则返回-1## SQRT SQRT ( )返回给定数字的平方根## RAND RAND ( )返回一个大于等于0且小于1的随机数,均匀分布。随机数在重新计算时发生变化。## RANDBETWEEN RANDBETWEEN ( , )返回指定数字之间的随机数## EVEN EVEN ( )向上舍入到最近的偶数数字。你可以使用此函数来处理成对出现的项。例如,包装板条箱接受一行或两行的货物。当物品的数量四舍五入到最近的两个,符合板条箱的容量时,板条箱就满了。=EVEN(1.5)— returns2=EVEN(3)— returns4=EVEN(2)— returns2=EVEN(-1)— returns -2## ODD ODD ( )向上舍入到最近的奇数数字=ODD(1.5)— returns3=ODD(3)— returns3=ODD(2)— returns3=ODD(-1)— returns -1=ODD(-2)— returns -3## GCD GCD ( , )返回两个整数的最大公约数。最大公约数是除1和2而没有余数的最大整数。## LCM LCM ( , )返回整数的最小公倍数。最小公倍数是最小的正整数,它是两个整数参数1和2的倍数。使用LCM添加不同分母的分数。## QUOTIENT QUOTIENT ( , )返回除法的整数部分QUOTIENT(0,2)— returns0QUOTIENT(1,2)— returns0QUOTIENT(4,2)— returns2QUOTIENT(5,2)— returns2QUOTIENT(6,2)— returns3QUOTIENT(6,3)— returns2QUOTIENT(7,3)— returns2# 数学函数实例 最后,有几个四舍五入函数值得举例说明;实际上,你可以使用几种方法来获得相同的结果。下图中公式和列将一起展示,如图所示:FLOOR =FLOOR(Tests[Value],0.01)TRUNC =TRUNC(Tests[Value],2)ROUNDDOWN =ROUNDDOWN(Tests[Value],2)MROUND =MROUND(Tests[Value],0.01)ROUND =ROUND(Tests[Value],2)CEILING =CEILING(Tests[Value],0.01)ISO.CEILING = ISO.CEILING(Tests[Value],0.01)ROUNDUP =ROUNDUP(Tests[Value],2)INT =INT(Tests[Value])FIXED =FIXED(Tests[Value],2, TRUE)05 基础函数类型 - 图5不同舍入函数的效果汇总如你所见,除了可以指定四舍五入的位数,FLOOR、TRUNC和ROUNDDOWN在其他方面非常相似。而CEILING和ROUNDUP是结果非常相似的两个函数。你还可以看到MROUND和ROUND这两个函数之间舍入的方式有一些不同。

| # 05.09 信息函数 来源网址:https://www.powerbigeek.com/what-is-dax/作者: | | —- |

了解信息函数

信息函数可以用来分析表达式的类型,所有这些函数都以 IS- 开头,返回一个TRUE/FALSE值,可以在任何逻辑表达式中使用。## ISBLANK ISBLANK ( )检查值是否为空,并返回TRUE或FALSE## ISERROR ISERROR ( )检查值是否为错误,并返回TRUE或FALSE## ISLOGICAL ISLOGICAL ( )检查值是否是逻辑值(TRUE或FALSE),并返回TRUE或FALSE## ISNONTEXT ISNONTEXT ( )检查值是否为非文本(空白单元格不是文本),并返回TRUE或FALSE## ISNUMBER ISNUMBER ( )检查值是否为数字,并返回TRUE或FALSE## ISTEXT ISTEXT ( )检查值是否为文本,并返回TRUE或FALSE当使用列 (而不是表达式) 作为参数时, 函数 ISNUMBER、ISTEXT 和 ISNONTEXT 始终返回 TRUE 或 FALSE, 具体结果取决于列的数据类型和每个单元格的空值类型。## ISEMPTY ISEMPTY()检查表或表表达式是否为空。使用COUNTROWS统计表的行数也可以检查表是否为空,但这种做法的弊端在于当表不为空时,公式还计算出了它的行数,对于只需要判断是否为空的情况,这种做法产生了额外开销。而ISEMPTY的写法更简单、更直观且性能更好。ISEMPTY(CALCULATETABLE(‘Internet Sales’,’Product Category’[Product Category Name]))检测表是否为空的三种表达式(按使用优先级排序):

  1. ISEMPTY ( )
  2. ISBLANK ( COUNTROWS (
  3. ) )
  4. COUNTROWS (
  5. ) = 0 注:Excel 2013不支持ISEMPTY函数# 信息函数实例 你可能想知道是否可以将ISNUMBER与文本列一起使用,以检查是否可以将其转换为数字。不幸的是,你不能使用这种方法;如果要测试文本值是否可转换为数字,必须尝试转换并在失败时处理错误。例如,要测试文本类型的价格列是否包含有效数字,公式必须写成:IsPriceCorrect =NOT(ISERROR(Sales[Price]+0))公式向价格中添加零,以强制从文本值转换为数字;如果成功, 则它将返回 TRUE (因为 ISERROR 将返回 FALSE), 否则它将返回 FALSE (因为 ISERROR 返回 TRUE),转换将失败。例如, 如果价格列的某些行使用了 “N/A” 字符串。但是, 如果你尝试使用 ISNUMBER, 例如下面的表达式, 将始终返回 FALSE:IsPriceCorrect =ISNUMBER(Sales[Price])在这种情况下,ISNUMBER总是返回FALSE,因为根据元数据,价格列不是数字,而是字符串类型,不管每一行的内容如何。