在 Power BI 漩涡中——了解循环依赖

循环依赖错误是 Power BI 数据建模中最烦人的事情之一!了解为什么会发生这种情况以及如何避免这种情况——正如您可能已经阅读我之前的文章所了解的那样,创建星型模式并不一定意味着您的数据建模任务已经完成。还有更多方面需要注意——而且,虽然你可以在不微调每个细节的情况下偷偷摸摸——其中一些“细节”是……

在 Power BI 漩涡中——了解循环依赖

循环依赖错误是 Power BI 数据建模中最烦人的事情之一!了解为什么会发生这种情况以及如何避免它

您可能已经了解阅读我之前的文章,创建星型模式并不一定意味着您的数据建模任务已完成。还有更多方面需要注意——虽然你可以在不微调每个细节的情况下偷偷摸摸——其中一些“细节”当然更重要,需要更好地理解。

其中一个很容易把你拉进“漩涡”的“细节”,叫做——循环依赖!我很确定我们所有人都至少有一次(希望只有一次)对以下消息感到恼火:“检测到循环依赖……”

但是,在我们继续解释什么是循环依赖以及为什么要关心它之前,让我们先了解一下……

什么是依赖……

顾名思义,依赖是一个事实,即一件事(或事件)依赖于另一件事的行为。

我给你举个简单的例子:假设巴士票价取决于燃料价格等因素。这意味着,如果燃油价格发生变化,巴士票价也会发生变化。

Bus Ticket Price = [Fuel Price] + [Factor A] + [Factor B] + ... [Factor N]

这是一种所谓的常规依赖,它存在于每一种编程语言中(也存在于日常生活中)。但是,如果巴士票价发生变化怎么办?这是否一定意味着燃油价格也会发生变化?不!巴士票价取决于燃油价格,反之则不然!

我为什么这么问?因为在某些情况下,两个事实或事件之间可能存在相互依赖关系。

假设您想降低燃料桶的运输成本。实现这一目标的潜在步骤之一是降低燃料成本(因为燃料成本会影响整体运输成本)。因此,为了能够降低运输成本,我们需要降低燃料成本。同时,为了降低燃料成本,我们需要降低该燃料的运输成本。而且,这就是循环依赖……

如果事实 A 依赖于事实 B,同时事实 B 也依赖于事实 A,我们说的是循环依赖!

好的,既然我们知道了循环依赖是什么,让我们来看看它为什么会出现在 Power BI 中,以及我们如何消除它。正如您可能假设的那样,Marco 和 Alberto 已经写了一篇关于 DAX 中循环依赖的内部结构的优秀文章,所以我鼓励您去阅读它以更好地理解这个“特性”。[0]

让我们运行一个非常基本的用例,并借助 CALCULATE() 函数在我们的数据模型中创建一个计算列。

正如您在上图中所见,我有一个简单的数据模型,由两个表组成:事实表 Bets,它存储有关我们客户下注的数据;以及竞赛维度表,其中包含竞赛的属性。

假设我想用额外的计算列来丰富我的数据模型,这将只计算英超联赛的投注金额。轻松的工作!

Bets Premier League = 
CALCULATE(
SUM(Bets[Amount]),
Competition[Competition Name] = "Premier League"
)

而且,一旦应用,我的投注表如下所示:

这很酷,对吧?我们确信这是可行的,所以让我们尝试创建另一个相同的列:

等等,什么?!这不是一分钟前完美运行的公式吗?

让我们从数据模型中删除两个新列,切换到 DAX Studio,并尝试了解公式引擎生成的查询计划:[0]

只需注意逻辑查询计划的第 1 行,它告诉我们我们的新列依赖于 Bets 表中的所有现有列!我们希望新的 Bets Premier League 列依赖于 CompetitionID,这是我们的 Competition 表的外键,我们在其中应用我们的过滤。但是,事实并非如此。为什么会这样?!

当我们创建一个计算列时,我们的表达式被逐行评估(行上下文)。当我们在行上下文中使用 CALCULATE 时,该函数会应用上下文转换,因此我们对范围内的所有列都有一个过滤器!

简单来说,这是我们计算的伪代码:

Calculation: Bet Amount
--Filters
Bets[Bet ID] = 333
Bets[Bet Date] = 2021-11-06
Bets[Customer ID] = 2
Bets[Competition ID] = 789
Bets[Amount] = 200

而且,这很好用,正如您可能已经看到的那样。但是,当我们想用相同的公式创建另一列时会发生什么:

与前面的例子一样,NEW 列依赖于 Bets 表中的所有原始列——但是,它还依赖于我们之前创建的 Bets Premier League 列!由于此列是在刷新时计算的,因此 Power BI 无法解决此依赖关系并抱怨循环依赖关系。

简而言之,如果您能够在数据模型中创建这两列,那么刷新数据集后会发生以下情况:

Column Bets Premier League 将取决于所有源列 + Bets Premier League NEW 列。另一方面,Bets Premier League NEW 列将依赖于所有源列 + Bets Premier League 列。而且,这就是 Power BI 拒绝创建这样一个模型的原因,因为两个计算列之间存在循环依赖关系。

通过扩展原始列表达式并使用 REMOVEFILTERS 函数删除由于上下文转换而应用的所有过滤器,可以轻松解决此问题:

Bets Premier League NEW = 
CALCULATE(
SUM(Bets[Amount]),
Competition[Competition Name] = "Premier League",
REMOVEFILTERS(Bets[Bets Premier League])
)

应用后,Power BI 不再抱怨循环依赖,NEW 列成为数据模型的一部分:

好的,我们解决了这个挑战,但是让我向您展示如果我尝试在维度表中而不是在事实表中创建具有相同公式的计算列会发生什么。

我将转到 Competition 表并粘贴 Bets Premier League 计算列的代码:

这里到底发生了什么?!之前导致“循环依赖”错误的相同代码现在可以像魅力一样工作!

问题是,当表中存在具有唯一值的列时(在我们的例子中,竞争 ID 作为 Competition 表的主键),上下文转换被优化以避免过滤除具有唯一值的列之外的所有其他列。该列的唯一性由引擎在建立 Competition 表(在关系的 1 侧)和 Bets 表之间的 1:M 关系时确认。

但是,正如 SQL BI 的这篇文章中所解释的,这种在维度表中创建计算列以避免循环依赖问题的“技术”不是推荐的做法,因此应尽可能避免。[0]

Conclusion

循环依赖是 Power BI 数据建模过程中最烦人的事情之一!每当您创建两个相互依赖的对象时,您就有可能遇到此问题。有时,确定根本原因可能是一项简单的任务,但在某些情况下,有必要了解 DAX 处事方式的细微差别。

Thanks for reading!

不要错过 Medium 上的任何故事![0]

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2022年5月11日
下一篇 2022年5月11日