2.7 使用常见的DAX函数

第2章 DAX简介

2.1 理解DAX计算
2.2了解计算列和度量值
2.3 变量入门
2.4 表达式中的错误处理
2.5 格式化DAX代码
2.6 聚合函数和迭代函数
2.7 使用常见的DAX函数

使用常见的DAX函数

既然您已经了解了DAX的基础知识以及如何处理错误情况,下面将简要介绍DAX的最常用函数和表达式。

聚合函数

前面部分,我们介绍了基本聚合函数,例如SUMAVERAGEMINMAX,了解到SUMAVERAGE仅适用于数字列。

DAX还为从Excel继承的聚合函数提供了另一种语法,该语法将后缀A添加到函数的名称中,仅仅为了获得与Excel相同的名称和行为。这些函数仅对包含布尔值的列有用,TRUE为1,将FALSE为0。文本列始终被视为0,因此,无论列内容如何,​​如果对文本列使用MAXA函数,结果将始终为0。

此外,DAX在执行聚合时从不考虑空单元格。尽管这些函数可以在非数字列上使用而不会出现错误,但是它们的结果没有用,因为不会自动将文本列转换为数字。这些函数分别命名为AVERAGEACOUNTAMINAMAXA。我们建议您不要使用这些函数,为了与可能依赖当前性能的现有代码兼容,这些函数的性能将来也不会改变。

注意
尽管名称与统计函数相同,但是它们在DAX和Excel中的用法有所不同,因为在DAX中,列具有数据类型,并且其数据类型确定聚合函数的行为。Excel为每个单元格处理不同的数据类型,而DAX为整个列处理单个数据类型。DAX以表格形式处理每列具有明确定义类型的数据,而Excel公式可在没有明确定义类型的异构单元格值上工作。如果Power BI中的列具有数字数据类型,则所有值只能是数字或空单元格。如果列是文本类型,则对于这些函数,即使文本可以转换为数字,列始终为O(COUNTA除外)),而在Excel中,该值也被视为逐个单元格的数字。由于这些原因,这些函数对于“文本”列不是很有用。只有MIN和MAX也支持DAX中的文本值。

您先前学习的对值进行聚合的函数很有用。有时,您可能对聚合值不感兴趣,而只是要进行计数。DAX提供了一组对计数行或值有用的函数:

  • COUNT除* Boolean 外, COUNT可以*对任何数据类型进行操作。
  • *COUNTA *可在任何类型的列上运行。
  • *COUNTBLANK *返回一列中的空单元格(空白或空字符串)的数量。
  • *COUNTROWS *返回表中的行数。
  • *DISTINCTCOUNT *返回一列的不同值的数量,如果存在则包括空白值。
  • *DISTINCTCOUNTNOBLANK *返回一列的不同值的数量,不包括空白值。

COUNT 和 COUNTA 在DAX中几乎是相同的函数。它们返回不为空的列值的数量,而不管其数据类型如何。它们从Excel继承,其中 COUNTA 接受任何数据类型,包括字符串,而 COUNT 仅接受数字列。如果我们要计算包含空值的列中的所有值,则可以使用 COUNTBLANK 函数。空格和空值被COUNTBLANK视为空值。最后,如果我们要计算表的行数,则可以使用COUNTROWS函数。请注意,COUNTROWS需要表作为参数,而不是列。

注意
DISTINCTCOUNT是DAX的2O12版本中引入的函数。DAX的早期版本不包含DISTINCTCOUNT。要计算列中不重复值的数量,我们必须使用COUNTROWS(DISTINCT(table [column]))。尽管DISTINCTCOUNT更易于阅读,且只需要一个函数调用,这两种模式返回相同的结果。DISTINCTCOUNTNOBLANK是 2O19中引入的函数,它提供与SQL中COUNT DISTINCT操作相同的语义,而无需在DAX中编写更长的表达式。

最后两个函数DISTINCTCOUNTDISTINCTCOUNTNOBLANK很有用,因为它们完全按照其名称的含义进行操作:计算列的唯一值,该列将其作为唯一参数。DISTINCTCOUNTBLANK值视为可能的值之一,而DISTINCTCOUNTNOBLANK则忽略BLANK值。

逻辑函数

有时我们想在表达式中建立逻辑条件---例如,根据某列的值来选择执行不同的计算或拦截错误。对此,我们可以使用DAX中的逻辑函数。前面标题为“处理DAX表达式中的错误”的章节阐述了该组的两个最重要的函数:IFIFERROR。本章前面的“条件语句”部分阐述了IF函数。

逻辑函数非常简单,作用如同其名字,它们是ANDFALSEIFIFERRORNOTTRUEOR。例如,如果仅当Price列包含数字值时才将数量乘以价格作为金额,则可以使用以下模式:
Sales[Amount] = IFERROR ( Sales[Quantity] * Sales[Price], BLANK ( ) )

如果我们不使用IFERROR,并且如果Price列包含无效数字,则计算列的结果将是错误,因为如果单行生成计算错误,则该错误会传播到整个列。但是,使用IFERROR可以截获错误并将其替换为空白值。

该类别中另一个有趣的函数是SWITCH,当列中包含少量不重复值时,该函数非常有用,我们希望根据其值获得不同的行为。例如,列SizeProduct表中包含S,M,L,XL,我们可能要在另列对其进行更明确描述。我们可以使用嵌套的IF调用获得结果:

'Product'[SizeDesc] =
IF (
    'Product'[Size] = "S",
    "Small",
    IF (
        'Product'[Size] = "M",
        "Medium",
        IF (
            'Product'[Size] = "L",
            "Large",
            IF (
                'Product'[Size] = "XL",
                "Extra Large",
                "Other"
            )
        )
    )
)

以下使用更方便的方法来表达相同的公式,用*SWITCH *,是这样的:

'Product'[SizeDesc] =
SWITCH (
    'Product'[Size],
    "S", "Small",
    "M", "Medium",
    "L", "Large",
    "XL", "Extra Large",
    "Other"
)

后一个表达式中的代码更具可读性,但不是更快,因为内部DAX将SWITCH语句转换为一组嵌套的IF函数。

注意
SWITCH通常用于检查参数值和定义度量值的结果。例如,可以创建一个包含三行的YTD,MTD,QTD的参数表,然后让用户从三个可用的聚合中选择要在度量中使用的聚合。这是2019年之前的常见情况。现在由于有了第9章中介绍的计算组,因此不再需要它。计算组是计算用户可以参数化计算值的首选方式。

提示
这是一种使用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作为第一个参数表示:"条件评估为TRUE时将第一个结果返回"。

信息函数

当需要分析表达式的类型时,可以使用一个信息函数。所有这些函数都返回一个布尔值,并且可以在任何逻辑表达式中使用。它们是ISBLANKISERRORISLOGICALISNONTEXTISNUMBERISTEXT

重要的是要注意,当将列作为参数而不是表达式传递时,函数ISNUMBERISTEXT ISNONTEXT 始终返回 TRUE FALSE,这取决于列的数据类型以及单元格是否为空。这使得这些函数在DAX中几乎毫无用处。DAX第一版已从Excel中继承这些函数。

您可能想知道可不可以在文本列中使用ISNUMBER来检查该列是否可以转换为数字。不幸的是,这种方法是不可以的。如果要测试文本值是否可以转换为数字,则必须尝试进行转换,如果转换失败,则应处理错误。例如,要测试Price列(类型为string)是否包含有效数字,必须输入

Sales[IsPriceCorrect] = NOT ISERROR ( VALUE ( Sales[Price] ) )

DAX尝试将字符串值转换为数字。如果成功,则返回TRUE(因为ISERROR返回FALSE)。否则,它返回FALSE(因为ISERROR返回TRUE)。例如,如果某些行的价格字符串值为" N / A",则转换失败。

但是,如果我们尝试使用ISNUMBER,如以下表达式所示,则结果始终为FALSE

Sales[IsPriceCorrect] = ISNUMBER ( Sales[Price] )

在这种情况下,ISNUMBER始终返回FALSE,因为基于模型中的定义,Price列不是数字,而是字符串,而与每一行的内容无关。

数学函数

DAX中可用的数学函数集与Excel中可用的函数集相似,具有相同的语法和行为。

常用的数学函数是ABSEXPFACTLNLOGLOG10MODPIPOWERQUOTIENTSIGN,和SQRT。随机函数是RANDRANDBETWEEN。通过使用EVENODD,您可以测试数字。GCDLCM可用于计算两个数字的最大公约数和最小公倍数。QUOTIENT返回两个数字的整数除商。

最后,是关于几个舍入函数的示例。实际上,可以使用几种方法来获得相同的结果。图2-8列出了一些计算列及计算结果:

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 )
图2-8 此摘要显示了使用不同舍入函数的结果

*FLOOR TRUNC ROUNDDOWN 相似,不同之处在于我们可以指定要舍入的位数。在相反的方向上, CEILING ROUNDUP 的结果相似。您会发现 MROUND ROUND *函数之间的舍入方式存在一些差异。

三角函数

DAX提供了一组丰富的三角函数:COSCOSHCOTCOTHSIN,SINHTANTANH。给这些函数加前缀A即成为该函数的反函数(arcsine,arccosine等)。由于它们的用法很简单,因此我们不赘述。

DEGREES RADIANS分别执行度和弧度的转换,* SQRTPI *将其参数乘以pi后计算平方根。

文本函数

DAX中可用的大多数文本函数与Excel中可用的文本函数相似,只有少数例外。文本函数为CONCATENATECONCATENATEX,EXACTFINDFIXEDFORMATLEFTLEN,LOWERMIDREPLACEREPTRIGHTSEARCHSUBSTITUTETRIMUPPER和*VALUE *这些函数对于处理文本和从包含多个值的字符串中提取数据很有用。例如,图2-9 显示了一个示例示例,该示例从以逗号分隔的字符串中提取名字和姓氏,去掉中间的称呼。

图2-9 本示例显示了使用文本函数提取的名字和姓氏

为了获得结果,先计算两个逗号的位置。然后,我们使用这些数字提取文本的右侧部分。该SimpleConversion执行计算公式,如果字符串中少于两个逗号可能返回不正确的值,如果根本没有逗号就会报错。FirstLastName执行一个更复杂的表达式,缺少逗号的情况下也不会失败。

People[Comma1] = IFERROR ( FIND ( ",", People[Name] ), BLANK ( ) )
People[Comma2] = IFERROR ( FIND ( " ,", People[Name], People[Comma1] + 1 ), BLANK ( ) )
People[SimpleConversion] =
MID ( People[Name], People[Comma2] + 1, LEN ( People[Name] ) )
    & " "
    & LEFT ( People[Name], People[Comma1] - 1 )
People[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会执行数据类型的自动转换来满足操作符的要求。尽管转换自动进行的,但是仍然可以通过一组函数执行显式的数据类型转换。

CURRENCY可以将表达式转换为Currency类型,而INT则将表达式转换为Integer。DATETIME将日期和时间部分作为参数,并返回正确的DateTimeVALUE将字符串转换为数字格式,而FORMAT将数字值作为其第一个参数,并将字符串格式作为其第二个参数,并且可以将数字值转换为字符串。FORMAT通常与DateTime一起使用。例如,以下表达式返回"2019 Jan 12":

= FORMAT ( DATE ( 2019, 01, 12 ), "yyyy mmm dd" )

使用DATEVALUE函数执行相反的操作,即将字符串转换为DateTime值。

DATEVALUE与不同格式的日期
DATEVALUE显示不同格式日期的特殊性。在欧洲标准中,日期以" dd / mm / yy"格式书写,而美国人更喜欢使用" mm / dd / yy"格式。例如,2月28日在两种文化中具有不同的字符串表示形式。如果您向DATEVALUE提供不能使用默认区域设置进行转换的日期,不是立即引发错误,它会尝试第二次转换月份和日期。DATEVALUE支持明确的格式" yyyy-mm-dd"。例如,无论您使用哪种区域设置,以下三个表达式的计算结果均为2月28日。

DATEVALUE ( "28/02/2018" ) -- This is February 28 in European format
DATEVALUE ( "02/28/2018" ) -- This is February 28 in American format
DATEVALUE ( "2018-02-28" ) -- This is February 28 (format is not ambiguous)

有时,DATEVALUE在预期它会发生错误的时候却不发生错误。这是设计使然。

日期和时间函数

在几乎每种类型的数据分析中,处理时间和日期都是工作的重要部分。许多DAX函数都按日期和时间运行。其中一些对应于Excel中的类似函数,并且可以简单地与DateTime数据类型进行转换。日期和时间函数DATEDATEVALUEDAYEDATEEOMONTHHOURMINUTEMONTHNOWSECONDTIMETIMEVALUETODAYWEEKDAYWEEKNUMYEARYEARFRAC

这些函数对于基于日期计算值很有用,但是不用于执行典型的时间智能计算,例如,按年比较汇总值或计算度量值的年初至今值。要执行时间智能计算,可以使用另一组时间智能函数,我们将在第8 章“时间智能计算”中进行介绍。

如本章前述,DateTime数据类型在内部使用浮点数,其中整数部分对应于1899年12月30日之后的天数,而小数部分则表示日期中多出的时间部分,小时、分钟和秒将转换为小数部分。因此,将整数加到*DateTime *值将会值增加相应的天数。你会发现使用转换函数从日期中提取日期、月份和年份会更方便。

'Date'[Day] = DAY ( Calendar[Date] )
'Date'[Month] = FORMAT ( Calendar[Date], "mmmm" )
'Date'[MonthNumber] = MONTH ( Calendar[Date] )
'Date'[Year] = YEAR ( Calendar[Date] )
图2-10本示例说明如何使用日期和时间函数提取日期信息

关系函数

可用于在DAX公式内的关系之间导航的两个有用函数是RELATEDRELATEDTABLE

您已经知道,计算列可以引用定义它的表的列值。因此,在Sales表中定义的计算列可以引用Sales表的任何列。但是,如果必须引用另一张表中的列怎么办?通常,除非模型在两个表之间定义了关系,否则不能使用其他表中的列。如果这两个表共享一个关系,您可以使用RELATED函数来访问关联表中的列。

例如,要计算Sales表中的计算列,以检查已售出的产品是否属于"手机"类别,如果是,将折减系数应用于标准成本。要计算此列,必须使用一个条件来检查产品类别的值,该值不在Sales表中。但是,关系链从Sales开始,通过ProductProduct Subcategory达到*Product Category *如图2-11 所示:

图2-11 Sales与Product Category具有连锁关系

无论从原始表到相关表需要多少步骤,DAX都会遵循完整的关系链,并返回相关的列值。因此,AdjustedCost列的公式如下所示:

Sales[AdjustedCost] =
IF (
    RELATED ( 'Product Category'[Category] ) = "Cell Phone",
    Sales[Unit Cost] * 0.95,
    Sales[Unit Cost]
)

在一对多关系中,RELATED可以从多侧访问一侧,因为在这种情况下,相关表中仅存在一行(如果有)。如果不存在这样的行,则RELATED返回BLANK

如果表达式在关系的“一”侧,并且需要访问“多”侧,则RELATED不会有帮助,因为另一侧可能有许多行可用于单个行。在这种情况下,我们可以使用RELATEDTABLE。RELATEDTABLE返回一个包含所有与当前行相关的行的表。例如,如果我们想知道每个类别中有多少个产品,则可以使用以下公式在Product Category*中创建一列:
'Product Category'[NumOfProducts] = COUNTROWS ( RELATEDTABLE ( Product ) )

对于每个产品类别,此计算列显示相关的产品数量,如图2-12 所示


图2-12 运用RELATEDTABLE计算产品数量

如上所示,RELATEDRELATEDTABLE可追循从“一”侧开始朝着“多”侧的关系链。RELATEDTABLE通常与迭代函数结合使用。例如,如果要计算每个类别的数量总和乘以净价,则可以编写一个新的计算列,如下所示:

'Product Category'[CategorySales] =
SUMX (
    RELATEDTABLE ( Sales ),
    Sales[Quantity] * Sales[Net Price]
)

该计算列的结果如图2-23所示:


图2-13 使用* RELATEDTABLE*和迭代函数计算每个类别的销售额

由于该列是经过计算的,因此结果将合并到表中,并且不会根据报告用户的选择而更改,就像将其写入度量值一样。

结论

在本章中,您学习了许多新函数,并开始研究一些DAX代码。您可能不会马上记住所有函数,但是使用的函数越多,对函数就会变得越熟悉。

您在本章中学到的更重要的主题是:

  • 计算列是表中使用DAX表达式计算的列。计算列在数据刷新时计算,并且不会根据用户选择更改其值。
  • 度量值DAX表达式定义的计算。度量值不是像计算列那样在刷新时计算,而是在查询时计算。因此,度量值取决于用户在报告中的选择。
  • DAX表达式中的任何时间都可能发生错误,最好事先检测错误状况,而不是让错误发生并在事实发生后拦截它。
  • 像SUM这样的聚合函数对于聚合列很有用,而对于聚合表达式,则需要使用迭代函数。迭代函数通过扫描表并逐行评估表达式来工作。在迭代结束时,迭代函数根据其语义聚合结果。
  • 在下一章中,您继续学习DAX中最重要的表函数。

你可能感兴趣的:(2.7 使用常见的DAX函数)