PowerBI DAX如何高效处理无效的计算?

学习了些DAX基本语法,但是有时在引用的数据对公式无效时,就可能会发生无效的计算,会出现一些无效错误,比如转化错误,空值错误等等,像这种情况怎么高效处…
关注者
2
被浏览
3,962

1 个回答

我是BI佐罗(微软 Power BI MVP),相信现在您已经了解了语法的一些基本知识,而你接下来应该学习如何优雅地处理无效的计算。当引用的数据对公式无效时,就可能会发生无效的计算。例如,公式中包含除以零的除法,或者在乘法时引用了非数字形的列值。最好了解这些错误在默认情况下是如何处理的,以及如何截获这些错误。 (文末有赠送Power BI 学习资料)

在学习如何处理错误之前,我们先描述 DAX 公式计算过程中可能出现的各种错误。它们是:

  • 转换错误
  • 算术运算错误
  • 空值或缺失值

转换错误

第一种错误是转换错误。如本章前面所述,DAX 会在运算需要时自动转换字符串和数字的值。以下示例都是有效的 DAX 表达式:


"10" + 32 = 42
"10" & 32 = "1032"
10 & 32 = "1032"
DATE (2010,3,25) = 3/25/2010
DATE (2010,3,25) + 14 = 4/8/2010
DATE (2010,3,25) & 14 = "3/25/201014"

这些公式是正确的,因为它们使用常数值运算。但是,如果 VatCode 是一个字符串,那么下面的公式結果是什么呢?


Sales[VatCode] + 100

由于这个求和表达式的第一个运算对象是文本类型,所以你必须确保 DAX 可以将该列中的所有值转换为数字。如果 DAX 无法将某些内容转换为适合运算需要的内容,就会发生转换错误。下面是一些典型的情况:


"1 + 1" + 0 = 无法将文本类型的值"1 + 1"转换为数字类型
DATEVALUE ("25/14/2010")  = 类型不匹配

如果要避免这些错误,重要的是在 DAX 表达式中添加对错误的检测逻辑,以拦截错误条件并返回有意义的结果。人们可以在错误发生后截取错误,或者在事前检查可能会有错误情况的运算值。主动检查错误情况要好过让错误发生再截获它。

算术运算错误

第二类错误是算术运算,例如除零或负数的平方根。这些错误与转换无关:当你尝试用无效值去调用函数或执行运算时,DAX 都会提示这些错误。

除以零需要特殊处理,因为它的行为并不直观 (可能对数学家来说除外) 。当你把一个数除以零时,DAX 会返回一个无穷大的特殊值 Infinity。此外,在 0 除以 0 或无穷大除以无穷大的特殊情况下,DAX 返回特殊的 NaN(而不是数字值) 。

由于这是一种不寻常的行为,因此我们在表 2-3 中进行了总结。

表 2-3 除以零的特殊结果值


表达式 结果


需要注意的是,Infinity 和 NaN 不是错误,而是 DAX 中的特殊值。如果你将一个数除以无穷大,表达式不会产生错误。但会返回 0:


9954 / ( 7 / 0 ) = 0

除了这种特殊情况之外,DAX 在调用具有错误参数的函数会返回计算错误,如负数的平方根:


SQRT(-1) // 函数"SQRT"的参数数据类型错误,或者结果太大或太小

如果 DAX 检测到这样的错误,它就会阻止表达式的任何进一步计算,并引发错误。可以使用 ISERROR 函数检查表达式是否导致错误。我们将在本章后段展示。

请记住,像 N a N 这样的特殊值在多个工具(如 Power BI)的用户界面中显示为常规值。但是在某些客户端工具(如 Excel 数据透视表)显示时,它们可能会显示为错误。此外,错误检测函数也会将这些特殊值检测为错误。

空值或缺失值

我们研究的第三个类别不是特定的错误条件,而是空值的存在。当与计算中的其他元素组合时,空值可能会导致意外的结果或计算错误。

DAX 使用空值(BLANK) 处理缺失值、空白值或空单元格。空值不是一个真正的值,而是一种识别这些条件的特殊方法。我们可以通过调用 BLANK 函数来获得 DAX 表达式中的空值,这与空字符串不同。例如,下面的表达式会返回一个空值,可以在不同的客户端工具中显示为空字符串或 “(blank) ” :


= BLANK ()

这个表达式本身是无用的,但是每当需要返回空值时,BLANK 函数就变得有用了。例如,你可能希望显示一个空单元格,而不是 0。下面的表达式计算销售交易的总折扣,如果折扣为 0,则单元格为空 :


=IF (
    Sales[DiscountPerc] = 0,
    -- 确认是否有折扣
    BLANK (),
    -- 如果不存在折扣,返回空值
    Sales[DiscountPerc] * Sales[Amount]

BLANK 本身不是一个错误,它只是显示为空白结果。包含空白的表达式可能返回值或空白,具体取决于所需的计算。例如,当 Sales[Amount]为空时,下面的表达式将返回空:


= 10 * Sales[Amount]

换句话说,只要有一项或两项为空,乘积的结果就是空*。 当需要检查空白值时,这会产生挑战。由于隐式转换,无法使用相等运算符区分表达式是 0(或空字符串)还是空值 。*以下逻辑条件始终为真:


BLANK () = 0      -- 始终返回 TRUE
BLANK () = ""     -- 始终返回 TRUE

因此,如果 Sales[DiscountPerc] Sales[Clerk] 为空,则即使分别针对 0 和空字符串做测试,以下条件也会返回 TRUE:


Sales[DiscountPerc] = 0  
-- 如果 PercSales[DiscountPerc] 为 BLANK 或 0,则返回 TRUE
Sales[Clerk] = " "       
-- 如果 Sales[Clerk] 是 BLANK 或 "",则返回 TRUE

在这种情况下,可以使用 ISBLANK 函数检查值是否为空值*:*


ISBLANK ( Sales[DiscountPerc] ) 
-- 仅当 Sales[DiscountPerc]为空值时才返回 TRUE
ISBLANK ( Sales[Clerk] )      
-- 仅当 Sales[Clerk] 是空值时才返回 TRUE

在 DAX 表达式*中,*空值的传递发生在其他几个算术和逻辑操作中,如以下示例所示:


BLANK () + BLANK () = BLANK ()
10 * BLANK () = BLANK ()
BLANK () / 3 = BLANK ()
BLANK () / BLANK () = BLANK ()

然而,空值的传递并不适用于所有公式。某些计算不会传递空值。而是它们根据公式的其他项目返回一个值。这些示例包括加法、减法、空值作为除数,以及空值与有效值间的逻辑操作。以下显示了其中一些条件及其结果:


BLANK () − 10 = −10
18 + BLANK () = 18
4 / BLANK () = Infinity
0 / BLANK () = NaN
BLANK () || BLANK () = FALSE
BLANK () && BLANK () = FALSE
( BLANK () = BLANK () ) = TRUE
( BLANK () = TRUE ) = FALSE
( BLANK () = FALSE ) = TRUE
( BLANK () = 0 ) = TRUE
( BLANK () = "" ) = TRUE
ISBLANK ( BLANK() ) = TRUE
FALSE || BLANK () = FALSE
FALSE && BLANK () = FALSE
TRUE || BLANK () = TRUE
TRUE && BLANK () = FALSE
Excel 和 SQL 中的空值 Excel 用不同的方式来处理空值。在 Excel 中,所有空值在求和或乘法运算中都被当作 0,但如果它们是除法或逻辑表达式的一部分,则可能返回错误。 在 SQL 中,NULL 在表达式中的传递方式与 DAX 中的空值不同。正如您在前面的示例中所看到的,DAX 表达式中的空值并不总是导致空白结果,而 SQL 中的 NULL 对于整个表达式来说通常都是计算为 NULL。 这种差异在 DirectQuery 的时候是攸关的,因为一些计算是在 SQL 中执行的,而另一些是在 DAX 中执行的。两个引擎中对 BLANK 的语义不同可能导致意外的行为。

理解 DAX 表达式中空值或缺失值的行为,并使用 BLANK 函数返回空单元格,是控制 DAX 表达式结果的重要技能。在检测不正确的值或其他错误时,我们通常可以使用 BLANK 函数来处理,我们在下一节中介绍。

拦截错误

现在我们已经详细描述了可能发生的各种错误,接下来我们会向您展示截获错误并纠正它们的方法,或者至少生成包含有意义的错误提示消息。DAX 表达式中是否存在错误,通常取决于表达式本身中引用的表的和列中的值。因此,您可能希望避免这些错误出现并返回出错讯息。标准的作法是检查表达式是否返回错误,如果返回,则用特定的消息或默认值替换这个错误。DAX 有一些函数可以用于此任务。

第一个是 IFERROR 函数,它类似于 IF 函数,但是它计算的是检查表达式是否返回错误,而不是去计算一个布尔条件。IFERROR 函数的两种典型用法如下:


= IFERROR ( 
  Sales[Quantity] * Sales[Price],
  BLANK ()
= IFERROR ( 
  SQRT ( Test[Omega] ), 
  BLANK () 

在第一个表达式中,如果 Sales[Quantity] Sales[Price] 是不能转换为数字的字符串,则返回的表达式为空值。否则,将返回 Sales[Quantity] Sales[Price] 的乘积。

生成错误

有时,错误仅仅是一个错误,公式不应该在出现错误时返回默认值。实际上,返回默认值最终会产生不正确的实际结果。例如,包含不一致数据的配置表应该生成的是无效的报告,而不是可能被误认为是正确的不可靠的数字。

此外,人们可能希望生成对用户更有意义的错误讯息,而不是产生一个错误。这样的消息可以帮助用户找到问题所在。

考虑一个场景 :需要计算在开尔文测量到的绝对温度的平方根,以在一个复杂的科学计算中近似地调整声速。显然,我们不期望温度是负数。如果这种情况是由于测量中的问题造成的,我们需要引出一个误差并停止计算。

在这种情况下,此代码是危险的,因为它隐藏了问题:


= IFERROR ( 
    SQRT ( Test[Temperature] ),