对于Filter的第二参数的思考

fyxsky 发布于 14 天前 6 次阅读


AI 摘要

Filter函数的第二参数是布尔表达式,但你是否想过,所有返回布尔值的DAX函数都能成为筛选利器?本文通过对比两种计算新客户的DAX写法,揭示CONTAINS等函数在筛选中的巧妙应用,帮你拓展算法思路,让Power BI的“筛选+计算”更高效。

小思考

最近一直在思考,对于Power Bi的使用来说,如果日常项目中用不到的函数,如何更好的去理解。

简单地说,如果日常项目用加法就可以完成,那么可能不会发现乘法可以更好地优化计算。

对于这个问题,我觉得可能更好的方式有几点:

  1. 自己给自己出题。
  2. 没事就看看别人的代码。
  3. 不停的总结整理,从更方便自己理解的角度去做整理。

正题

最近想要好好理解一下Filter函数,在此过程中,看到一个别人写的代码。觉得很有意思,虽然代码本身没什么难度,但是代码里有一个我觉得我如果不遇到特定的问题,是无法想到的计算方式。

所以这里整理出来。

计算的目的 & 结果

计算当月产生购买的顾客中,有多少是新客户(第一笔购买发生在当月)

Raw data 如下,总行数大概59万行:

image-20221019122829842

用了两种计算方式 (加入了注释,方便理解,嗯,说的就是你,写给你看的。)

CC_A =
VAR AllCustomer = VALUES( ZSMnet[CustomerKey] )
VAR OldCustomer =
    FILTER(
        AllCustomer,
        // 注意Filter是迭代函数,这一点非常重要。意味着下面的表达式均需要对‘AllCustomer’表进行逐行计算。
        // 先算出每个Customer的全局第一次订单日期,再与外部视觉对象中的年月中的第一次订单日期进行比较。
        CALCULATE(
            MIN( ZSMnet[BillingDate] ),
            ALLEXCEPT( ZSMnet, ZSMnet[CustomerKey] )
        // ALLEXCEPT ( <表名>, <列名>, [ <列名>, … ] ) : ALLEXCEPT 从第一个参数指定的表中删除筛选器,只保留后续参数指定的列中的直接筛选器。
        )
            < MIN( ZSMnet[BillingDate] )
            // 这里可能有人会觉得如果当前行客户当前月没有下单岂不是BillingDate为空么,需要注意AllCustomer其实是当前上下文中的YearMonth下的已下单客户。
    )
RETURN
    COUNTROWS( EXCEPT( AllCustomer, OldCustomer ) )
    // EXCEPT ( <左表>, <右表> ) : 返回一个表,其中包含左表的行减去右表的所有行而得到的行。
CC_B =
COUNTROWS(
    FILTER(
        ADDCOLUMNS(
            VALUES( ZSMnet[CustomerKey] ),
            "DateOfFirstBuy",
                CALCULATE(
                    MIN( ZSMnet[BillingDate] ),
                    ALLEXCEPT( ZSMnet, ZSMnet[CustomerKey] )
                )
        ),
        // 注意Filter是迭代函数,这一点非常重要。意味着下面的表达式均需要对上面虚拟表进行逐行计算。
        CONTAINS(
            // CONTAINS ( <表>, <列名>, <值>, [ <列名>, <值> ], [ … ] )
            // 如果每个指定的 <值>可以在对应的<列名>列中找到,或包含在这些列中,则结果为 TRUE;否则函数返回 FALSE 。
            CALCULATETABLE(
                VALUES( ZSMnet[BillingDate] ),
                ALL( ZSMnet[CustomerKey] )
            ),
            ZSMnet[BillingDate],
            [DateOfFirstBuy]
        )
    )
)

计算结果 & 性能分析器

计算结果没什么好说的,肯定都是相同的。

image-20221019123210001

性能分析器,这个就有点意思了:可以看到CC_B的DAX查询性能基本上都是CC_A的2-3倍。

image-20221019123312760

思考

首先是DAX代码,CC_A的写法,基本上是最常见的,下意识的写法。

简单说就是,客户全局首次下单日期,和当月首次下单日期,比大小,就知道当前月下单是否是全局第一次下单。

CC_B的写法,就有点意思了。

简单地说就是,List 当前月所有客户的Billing Date,然后看当前行的Customer的全局首次下单日期,是否在这个 List 中。如果在则表示当前行客户在当前行年月是首次下单,属于新客户。

对于代码,能说的其实就这些。

但是我对这段代码最大的感触倒不完全是代码作者用到了CONTAINS函数来解决这个问题,而是我觉得,Filter 函数的第二参数 ( 布尔表达式 ),这是一个非常重要,但是又常常被我忽略的知识。

FILTER ( <表>, <布尔表达式> )

我相信大部分用Power BI的人都知道Filter的第二参数是布尔表达式,同样,我也知道。

但是我的Point是,如果把DAX所有返回值为布尔值的函数整理出来,也许在解决一些需求的时候,可以拓展很多算法思路。

因为从我的感觉来说,Power BI其实就是 筛选 + 计算。

第一步是如何把你需要计算的 Line 筛选出来,第二步计算反而不是很困难的。

附表

DAX函数 语法 返回值
TRUE TRUE() 标量 一个布尔值,始终返回 TRUE
PATHCONTAINS PATHCONTAINS ( , ) 标量 一个布尔值
OR OR ( , ) 标量 一个布尔值(True 或 False)
ISTEXT ISTEXT ( ) 标量 一个布尔值
ISSUBTOTAL ISSUBTOTAL ( ) 标量 一个布尔值
ISSELECTEDMEASURE ISSELECTEDMEASURE ( , [ … ] ) 标量 一个布尔值
ISONORAFTER ISONORAFTER ( , , [], [,,] ... ) 标量 一个布尔值
ISODD ISODD ( ) 标量 一个布尔值
ISNUMBER ISNUMBER ( ) 标量 一个布尔值
ISNONTEXT ISNONTEXT ( ) 标量 一个布尔值
ISLOGICAL ISLOGICAL ( ) 标量 一个布尔值
ISINSCOPE ISINSCOPE ( ) 标量 一个布尔值
ISFILTERED ISFILTERED (
) 标量 一个布尔值
ISEVEN ISEVEN ( ) 标量 一个布尔值
ISERROR ISERROR ( ) 标量 一个布尔值
ISEMPTY ISEMPTY ( ) 标量 一个布尔值
ISCROSSFILTERED ISCROSSFILTERED (
) 标量 一个布尔值
ISBLANK ISBLANK ( ) 标量 一个布尔值,如果值为空,则返回 TRUE;反之返回 FALSE
HASONEVALUE HASONEVALUE ( ) 标量 一个布尔值
HASONEFILTER HASONEFILTER ( ) 标量 一个布尔值
FALSE FALSE() 标量 一个布尔值,始终是 FALSE
EXACT EXACT ( , ) 标量 一个布尔值
CONTAINSSTRINGEXACT CONTAINSSTRINGEXACT ( , ) 标量 布尔值
CONTAINSSTRING CONTAINSSTRING ( , ) 标量 布尔值
CONTAINSROW 和 IN CONTAINSROW ( , , [ … ] ) 标量 布尔值,True 或 False
CONTAINS CONTAINS ( , , , [ , ], [ … ] ) 标量 一个布尔值
AND AND ( , ) 标量 一个布尔值(True 或 False)
最后更新于 2025-12-27