数据类型
DAX 可以使用七种常用的数据类型进行计算。在下面的列表中,我们展示了同一种数据类型在 DAX 种的名称和它更常见的名称。例如,布尔值(Boolean values)在 DAX 术语中被称为 TRUE/FALSE。我们更愿意遵循事实上的命名标准,将它们称为布尔值。除此之外,还有两种比较特殊的类型会在最后介绍
- 整数 (Integer)
- 十进制数 (Float)
- 货币 (Currency), 内部存储为整数的固定小数
- 日期 (DateTime)
- 布尔值 (TRUE/FALSE)
- 文本 (String)
- 二进制 (Binary)
- Blank/Null 类型
- 变体(Variant) 类型
例如,如果某列的数据类型是日期,那么求和的结果也是日期;然而,如果对整数使用相同的运算符,则结果是整数。这就是运算符重载,你可以在下图中看到它的行为示例,其中 OrderDatePlusOneWeek 列是通过在 Order Date 列的值上增加 7 来计算的。结果,就像我们说的,是一个日期。
除了运算符重载之外,在需要时 DAX 还会自动将字符串转换为数字,或者将数字转换为字符串。例如,如果使用连接字符串的&运算符,DAX 将把它的参数转换成字符串。例如:
= 5 & 4
将返回字符串形式的“54”,另一公式
= "5" + "4"
返回数值型结果 9
结果值依赖于运算符,而不是引用的列,列根据运算符的要求进行转换。即使这种行为看起来很方便,稍后你将看到在这些自动转换期间可能发生哪些错误。我们建议避免自动转换。如果需要进行某种转换,那么最好主动介入并使用显示转换。比如上面的例子应该使用:
= VALUE ( "5" ) + VALUE ( "4" )
熟悉 Excel 或其他语言的人可能更熟悉 DAX 数据类型。你可以在这里找到关于 DAX 数据类型的规范。然而,对于每一种数据类型介绍一些注意事项是很有必要的。
整数 (Integer)
DAX 只有一个整数数据类型,存储 64 位的整数。DAX 中的整数值之间的所有内部计算都使用 64 位整数。 它支持 19 位数;从 -9,223,372,036,854,775,807 (-2^63+1) 到 9,223,372,036,854,775,806 (2^63-2) 的正数或负数。 在需要控制舍入的情况下,整数类型非常有用。
十进制数(Float)
十进制数总是以双精度浮点值的形式存储。不要将这种 DAX 数据类型与 Transact-SQL 的十进制和数字数据类型混淆:在 SQL 中,DAX 十进制数字的对应数据类型是浮点。
货币(Currency)
货币数据类型存储固定的十进制数。它可以表示为 4 位的小数,内部存储为 64 位的整数值除以 10000。在货币数据类型之间执行的所有计算总是忽略 4 位小数点后面的小数。如果需要更精确的数据,则必须进行十进制数据类型的转换。
货币数据类型的默认格式包括货币符号。还可以将货币格式应用于整数和十进制数,还可以使用一种不带货币符号的格式来表示货币数据类型。
日期/时间(Datetime)
DAX 在日期/时间数据类型中存储日期。这种格式内部使用浮点数,其中整数对应于 1899 年 12 月 30 日以来的天数,而小数部分则表示当天的份数。小时、分钟和秒被转换成一天的小数部分。因此,下面的表达式返回当前日期加上一天(正好是 24 小时):
= NOW()+1
它的结果是在计算当前时间的明天的日期,如果你只需要使用日期/时间的日期部分,可以使用 TRUNC 函数来删除小数部分。
Power BI 还提供另外两种数据类型:日期类型和时间类型。在引擎内部,它们是日期/时间的简单变体。实际上,两种类型分别只存储日期/时间的整数部分或小数部分。
闰年错误
1983 年发布的流行电子表格程序 Lotus 1-2-3 在处理 DateTime 数据类型时出现了一个错误。它认为 1900 年是闰年,尽管事实并非如此(一个世纪的最后一年是闰年,前提是前两位数字除以 4 没有余数)。当时,Excel 第一版的开发团队故意复制了这个 bug,以保持与 Lotus 1-2-3 的兼容性。从那以后,出于兼容性考虑,Excel 的每一个新版本都把这个 bug 作为一个特性来维护。
为了保持与 Excel 的向后兼容,目前这个 bug 仍然在 DAX 中。Bug 的存在(或者我们应该称之为特性吗?)可能会导致 1900 年 3 月 1 日之前出现错误。因此,根据设计,DAX 的第一个官方支持的日期是 1900 年 3 月 1 日。在此日期之前执行的日期计算可能会导致错误,应该被认为是不准确的。
DAX 中的 1 代表 1899 年 12 月 31 日,而 EXCEL 是 1900 年 1 月 1 日,存在这种差别的原因是 Lotus 1-2-3 的最初版本有一个 bug,认为 1900 是一个闰年,其中包括 1900 年 2 月 29 日。实际上 1900 年不是闰年,所以在 1900 年 3 月 2 日之前的日期在 Lotus 1-2-3 中被误写了。由于莲花的电子表格在当时非常受欢迎,为了与其竞争,微软在自家的 EXCEL 上继承了这个 bug 以便用户可以无缝切换。DAX 的开发者对这个 Bug 采用了另一种不同的实现方式,导致在这个特定的时间段内,DAX 与 Excel 存在 1 天的差距。
如果你需要在 1900 年之前进行计算,你应该使用数学方法来将其移动到 1900 年之后的日期,执行计算,然后再平移回去。
布尔值(TRUE/FALSE)
布尔数据类型用于表示逻辑条件。例如,由以下表达式定义的计算列类型为布尔型:
= Sales[Unit Price] > Sales[Unit Cost]
你也可以将布尔数据类型视为数字,其中 TRUE=1 和 FALSE=0。这在排序时很有用,因为 TRUE >FALSE。相反的,如果在逻辑判断中直接使用数字,那么 0 将会被视为 FALSE,例如下面的表达式始终返回空
FILTER(Table,0)
文本(String)
DAX 中的每个字符串都存储为 16 位 Unicode 字符串。默认情况下,字符串之间的比较是不区分大小写的,因此这两个字符串“PowerPivot”和“POWERPIVOT”是相等的。
二进制(Binary)
二进制数据类型用于在数据模型中存储图像,在 DAX 中无法访问该数据类型。它主要被 Power View 或其他客户端工具用来显示直接存储在数据模型中的图片。在 Power BI 等其他工具中可能无法使用
空白/Null 类型
BLANK 不对应 SQL 中的 NULL。DAX 中的 BLANK 不遵循 NULL 在 SQL 中的计算逻辑。在中间结果可能是 BLANK 的表达式中,必须注意这种区别。 你可以使用 BLANK 函数创建空白,并使用 ISBLANK 对其进行测试。
关于空白、空字符串和零值的处理:
表达式 | DAX | Excel |
---|---|---|
BLANK + BLANK | BLANK | 0(零) |
BLANK + 5 | 5 | 5 |
BLANK * 5 | BLANK | 0(零) |
5/BLANK | 无穷大 | 错误 |
0/BLANK | NaN | 错误 |
空白/空白 | BLANK | 错误 |
FALSE OR BLANK | FALSE | FALSE |
FALSE AND BLANK | FALSE | FALSE |
TRUE OR BLANK | TRUE | TRUE |
TRUE AND BLANK | FALSE | TRUE |
BLANK OR BLANK | BLANK | 错误 |
BLANK AND BLANK | BLANK | 错误 |
关于 BLANK 的更多注意事项,请在阅读BLANK 的函数介绍。
变体/Variant 类型
变体数据类型用于可能返回不同数据类型的度量值,具体取决于度量值使用的条件表达式。例如,下面的语句可以返回整数或字符串,所以它返回一个变体类型:
IF ( [measure] > 0, 1, "N/A" )
DAX 运算符
了解了运算符在确定表达式类型的重要性之后,我们通过下表来看一下 DAX 中的运算符列表。
运算符列表
参数类型 | 符号 | 用法 | 举例 |
---|---|---|---|
小括号 | ( ) | 改变计算优先级和分组 | (5+2)*3 |
四则运算 | + | 相加 | 4 + 2 |
− | 相减/逻辑否 | 5 − 3 | |
* | 相乘 | 4 * 2 | |
/ | 相除 | 4 / 2 | |
比较符号 | = | 等于 | [CountryRegion] = “USA” |
<> | 不等于 | [CountryRegion] <> “USA” | |
> | 大于 | [Quantity] > 0 | |
>= | 大于或等于 | [Quantity] >= 100 | |
< | 小于 | [Quantity] < 0 | |
<= | 小于或等于 | [Quantity] <= 100 | |
== | 严格相等 | Product[Color] == BLANK() | |
文本连接 | & | 连接符 | “Value is” & [Amount] |
逻辑判断 | && | 两个布尔表达式之间的逻辑“与” | [CountryRegion] = “USA” && [Quantity]>0 |
|| | 两个布尔表达式之间的逻辑“或” | [CountryRegion] = “USA” || [Quantity] > 0 | |
IN | 元素包含在列表中 | [CountryRegion] IN {“USA”, “Canada”} | |
NOT | 否定判断 | NOT [Quantity] > 0 |
注意:IN 运算符 2016 年 11 月发布,可能无法在 Excel 2016 以及更早的版本中使用,详情参考IN 和 CONTAINSROW一文。
此外,逻辑运算符也可以作为 DAX 函数使用,语法非常类似于 Excel。例如,你可以这样写:
AND ( [CountryRegion] = "USA", [Quantity] > 0 ) OR ( [CountryRegion] = "USA", [Quantity] > 0 )
它们分别与下面的写法等价:
[CountryRegion] = "USA" && [Quantity] > 0 [CountryRegion] = "USA" || [Quantity] > 0
当你必须编写复杂条件时,使用函数代替运算符进行布尔逻辑运算变得非常有用。事实上,当需要格式化大量代码时,函数比运算符更容易格式化和读取。然而,函数的主要缺点是一次只能传入两个参数。如果需要计算两个以上的条件,就需要嵌套函数。
严格相等运算
扩展阅读:
高老师,计算列不也是DAX表达式吗?
高老师,文中提到的隐式转换和运算符重载不是一回事,是隐式转换包含运算符重载这种情况吗?
想请教老师,空字符串,blank,””,要怎么区分呢?
想请教老师:在日期类型那部分内容里提到的1900年3月1日的问题,具体可能会发生什么样错误?在使用DATE函数的时候,是可以返回类似1899年1月1日这样的日期的,那么前面提到的错误,会是在什么情况下发生的呢?谢谢。
严格相等这个很有帮助,我遇到过需要区分0和blank的情况,在网上找了答案,没有提到严格相等的。最后使用isblank来解决的。
高飞老师,我补充一下测试的结果 “在货币数据类型之间执行的所有计算总是忽略4位小数点后面的小数”
实际测试的结果是 小数点4位以后的小数,忽略的方式是四舍五入。