【DAX学习】2、理解计算列和度量值

image.png

1、DAX基础语法

DAX公式一般的格式是将表名用单引号括起来,后跟用方括号括起的列名,如下:

'Sales'[Quantity]

但是标准是在列引用中始终使用表名,在度量引用中始终避免使用表名。
在以后介绍了上下文转换之后,我们会了解到这个标准背后的基本原理

Sales[Quantity] * 2  -- 这是计算列的写法
[Sales Amount] * 2   -- 这是度量值的写法
'--'和'//'在DAX中表示单行注释
多行注释以'/*'开始,以'*/'结束。

1.1 DAX数据类型
DAX支持七种数据类型参与计算

image.png

DAX会在操作符需要时自动将字符串转换为数字,并将数字转换为字符串。
例如,如果我们使用连接字符串的&操作符,DAX将其参数转换为字符串。
下面的公式以字符串形式返回“54”:

= 5 & 4

另一方面,该公式返回一个值为9的整数结果:

= "5" + "4"

但是上述方式并不推荐,如果真想转换数据类型,推荐显式转换,上面的例子可以改为:

= VALUE ( "5" ) + VALUE ( "4" )

1.2 DAX操作符

image.png

1.3 构造表
在DAX中,我们可以直接在代码中定义匿名表
如果表只有一列,那么语法上只需要一个值列表—每一行一个值—由花括号分隔。
我们可以用括号分隔多行,如果表只有一列,括号是可选的。
例如,以下两个定义是等价的:

{ "Red", "Blue", "White" }
{ ( "Red" ), ( "Blue" ), ( "White" ) }

如果表有多列,括号是必需的。
每一列的所有行都应该有相同的数据类型;否则,DAX将自动将列转换为一种数据类型,这种数据类型可以容纳同一列的不同行中提供的所有数据类型。

{
( "A", 10, 1.5, DATE ( 2017, 1, 1 ), CURRENCY ( 199.99 ), TRUE ),
( "B", 20, 2.5, DATE ( 2017, 1, 2 ), CURRENCY ( 249.99 ), FALSE ), 
( "C", 30, 3.5, DATE ( 2017, 1, 3 ), CURRENCY ( 299.99 ), FALSE )
}

表构造函数通常与IN操作符一起使用。例如,:

'Product'[Color] IN { "Red", "Blue", "White" }
( 'Date'[Year], 'Date'[MonthNumber] ) IN { ( 2017, 12 ), ( 2018, 1 ) }

以下示例展示了使用IN操作符比较一组列(元组)
这种方式不能与比较运算符一起使用,所以以下写法是无效的

( 'Date'[Year], 'Date'[MonthNumber] ) = ( 2007, 12 )

但是,我们可以用以下方式来重写它

( 'Date'[Year], 'Date'[MonthNumber] ) IN { ( 2007, 12 ) }

1.4 条件语句

在DAX中,我们可以使用IF函数编写条件表达式。例如:

IF (
Sales[Quantity] > 1, "MULTI", "SINGLE"
)

IF函数有三个参数,但只有前两个是必需的。第三个选项是可选的,默认为空白。所以以下两种写法都是一样的

IF (
Sales[Quantity] > 1, Sales[Quantity]
)

IF (
Sales[Quantity] > 1, Sales[Quantity], BLANK ()
)

2、计算列和度量值的区别

2.1 计算列

计算列是添加到模型中的新列,但它不是从数据源加载的,而是通过使用DAX公式创建的。
计算列和表格中的其他列一样,还可以使用计算列来定义关系。
定义计算列的DAX表达式在计算列所属表的当前行上下文中操作。对列的任何引用都将返回该列的当前行值,我们不能直接访问其他行的值。所有计算的列都占用内存,并且都是在表处理期间计算的。

在创建复杂的计算列时,存放在内存非常有用。计算复杂计算列所需的时间始终是处理时间而不是查询时间,这样可以获得更好的用户体验。不过,要注意计算列使用了宝贵的RAM。
例如,如果我们有一个复杂的计算列公式,我们可能会试图将计算步骤拆分到不同的中间列中。尽管这种方式在项目开发期间很有用,但在生产中却是一个坏习惯,因为每个中间计算都存储在RAM中,浪费了宝贵的空间。
如果模型是基于DirectQuery的,那么行为就会有很大的不同。在DirectQuery模式下,当表格引擎查询数据源时,计算的列是动态计算的。这可能导致数据源执行大量查询,从而产生速度较慢的模型。

例如,我们可以利用下单时间和交货日期两列数据,两列相减得出出交付订单所需的天数,以下是计算列的公式:

Sales[DaysToDeliver] = INT ( Sales[Delivery Date] - Sales[Order Date] )

2.2 度量值
度量值是用聚合函数计算出来的值,它的结果只有一个,而计算列得出的结果是一列的值
例如,在Sales表中定义一些计算列来计算毛利率金额:

Sales[SalesAmount] = Sales[Quantity] * Sales[Net Price] 
Sales[TotalCost] = Sales[Quantity] * Sales[Unit Cost] 
Sales[GrossMargin] = Sales[SalesAmount] – Sales[TotalCost]
image.png

如果想显示毛利占销售额的百分比,可以使用以下公式创建一个计算列:

Sales[GrossMarginPct] = Sales[GrossMargin] / Sales[SalesAmount]

这个公式在行级别上计算正确的值,如下图所示,但是在总体总数级别上,结果显然是错误的。

image.png

可以看出来,551.46%是所有毛利占比的总和,这种计算方式显然是错误的,我们应该以所有毛利的总和除以所有销售额的总和
如果需要对聚合值进行操作,而不是逐行操作,则必须创建度量。我们使用:=来定义度量值,而不是等号=,以便在代码中更容易区分度量和计算列。
定义GrossMarginPct作为度量值后,结果是正确的,如下图所示。

GrossMarginPct := SUM ( Sales[GrossMargin] ) / SUM (Sales[SalesAmount] )
image.png

度量值和计算列都使用DAX表达式,不同之处在于计算的环境

计算列的值是在数据刷新期间计算的,它使用当前行作为上下文。结果不依赖于用户在报表上的操作。

度量值对当前上下文定义的数据聚合进行操作。例如,在透视表中,根据透视表的行过滤源表,并使用这些过滤器聚合和计算数据。换句话说,度量值总是在计算环境下对数据的聚合进行操作(关于计算上下文会在之后进一步说明)。

因此,当在度量值中使用SUM(Sales[SalesAmount])时,我们指的是筛选聚合的所有行的总和。但是,当我们在计算列中使用Sales[SalesAmount]时,我们指的是当前行中SalesAmount列的值。

需要在表中定义度量值,但是并不真正属于表格。实际上,我们可以将度量值从一个表移动到另一个表,而不会丢失其功能。

因为度量值在查询时计算,它不会消耗内存和磁盘空间。通常,当我们可以用两种方式表达计算时,度量值是首选。应该将计算列的使用限制在少数严格需要它们的情况下。具有Excel经验的用户通常更喜欢计算列而不是度量,因为计算列非常类似于在Excel中计算的方式。然而,在DAX中计算值的最好方法是通过度量值。

此系列是对The_Definitive_Guide_to_DAX第2版的学习笔记

欢迎关注微信公众号:纸上躬行君

你可能感兴趣的:(【DAX学习】2、理解计算列和度量值)