共用方式為


DAX 使用者定義函式 (預覽)

備註

DAX 使用者定義的函式目前處於 預覽階段。

DAX 使用者定義函數 (UDF) 可讓您封裝 DAX 邏輯並像任何其他 DAX 函數一樣重複使用它。 UDF 引進了新的 FUNCTION 關鍵字、選擇性 參數 (純量、表格和參考) 和 類型檢查 協助程式,讓編寫更安全、更清晰。 定義 UDF 之後,您可以在量值計算資料行視覺化計算,甚至其他使用者定義函數中使用它。 使用者可以集中業務規則、提高可維護性並隨著時間的推移安全地發展計算。 函式是您可以在查詢檢視和 DAX中建立和管理的第一類模型物件,而且可以在 [函式] 節點下的 [模型總管] 中檢視。

啟用使用者定義的功能

若要在桌面中試用 UDF:

  1. 前往 檔案 > 選項及設定 > 選項
  2. 選取 預覽功能 並檢查 DAX 使用者定義的功能
  3. 選取 [ 確定 ], 然後重新啟動 Power BI Desktop。

定義和管理使用者定義的功能

有數個位置可定義及管理功能:

  • DAX 查詢視圖 (DQV)。 在 DQV 中定義和修改函數。 DQV 也包含內容功能表 快速查詢 (評估、定義和評估,以及定義此模型中的所有函數),以協助您快速測試和管理 UDF。
  • TMDL 視圖。 UDF 也可以在 TMDL 中編寫和編輯。 TMDL 視圖還包括右鍵選單 腳本化 TMDL 至
  • 模型瀏覽器。 現有函數可以在模型總管的 函數 節點下檢視。

定義 UDF 時,請遵循下列命名需求:

函數名稱

  • 在模型中必須格式良好且唯一。
  • 可以包含句點作為命名空間分隔符(例如 Microsoft.PowerBI.MyFunc)。 不能以句點開始或結束,也不能有連續的句點。
  • 除了句點之外,名稱只能包含英數字字符或底線。 不允許使用空格或特殊字元。
  • 不得與內建 DAX 函數或保留字 (例如測量、函數、定義) 衝突。

參數名稱

  • 只能包含英數字元或底線。 不允許經期。
  • 不得是保留詞。

使用 DAX 查詢視圖

您可以在查詢檢視中 DAX 定義、更新及評估使用者定義的函式。 如需有關DAX查詢檢視的其他資訊,請參閱查詢DAX檢視

一般形式

DEFINE
    /// Optional description above the function
    FUNCTION <FunctionName> = ( [ParameterName]: [ParameterType], ... ) => <FunctionBody>

小提示

使用 /// 來描述函數。 單行 (//) 或多行 (/* */) 註解不會出現在 IntelliSense 函式描述中。

範例:簡單的稅務函數

DEFINE
    /// AddTax takes in amount and returns amount including tax
    FUNCTION AddTax = 
        ( amount : NUMERIC ) =>
            amount * 1.1

EVALUATE
{ AddTax ( 10 ) }
// Returns 11

儲存至模型

若要將 UDF 從查詢視圖 DAX 儲存到模型:

  • 按一下「 使用變更更新模型 」以儲存查詢中的所有 UDF。
  • 或按一下 更新模型:在 定義的函數上方新增函數以儲存單一 UDF。

Power BI Desktop 中查詢檢視的 DAX 螢幕擷取畫面,醒目提示您可以儲存使用者定義函式的兩個位置。第一個是視圖頂部的帶有更改的更新模型按鈕。第二個是程式碼編輯器中標示為 Update model: Add new function 的狀態行

使用 TMDL 檢視

您可以在 TMDL 檢視中定義和/或更新使用者定義的函式。 如需 TMDL 檢視的其他資訊,請參閱 TMDL 檢視

一般形式

createOrReplace
    /// Optional description above the function
    function <FunctionName> = ( [ParameterName]: [ParameterType], ... ) => <FunctionBody>

範例:簡單的稅務函數

createOrReplace
    /// AddTax takes in amount and returns amount including tax
    function AddTax = 
        (amount : NUMERIC) =>
            amount * 1.1

儲存至模型

按一下視圖頂端的 「套用」 按鈕,將指令碼中的所有 UDF 儲存至模型。

Power BI Desktop 中 TMDL 檢視的螢幕擷取畫面,醒目提示檢視頂端的 [套用] 按鈕。這是您可以儲存使用者定義函數的位置。

在 Power BI 專案中使用 TMDL 指令碼

使用 Power BI 專案時,UDF 也會包含在語意模型 TMDL 腳本中。 它們可以在functions.tmdl定義資料夾中找到。

Power BI 專案的 Visual Studio Code 螢幕擷取畫面。檔案總管開啟至語意模型資料夾。在程式碼編輯器中已開啟 'functions.tmdl'。顯示了三個函式:CustomerLifetimeValue、AverageOrderValue 和 AddTax。

使用模型探索器

您可以從「 函數」 節點下的「模型總管」檢視模型中的所有使用者定義函數。 如需模型總管的其他資訊,請參閱 模型總管

Power BI Desktop 中的模型總管面板,顯示展開的 [函式] 節點。列出三個使用者定義的函式:AddTax、AverageOrderValue 和 CustomerLifetimeValue。

查詢檢視中DAX,您可以在模型瀏覽器中透過 UDF 的滑鼠右鍵功能表中使用快速查詢,輕鬆定義和評估函數。

Power BI Desktop 中的模型總管窗格會顯示展開的 [函式] 節點。兩個操作功能表已開啟:第一個功能表提供快速查詢、重新命名、從模型中刪除、在報告檢視中隱藏、全部取消隱藏、全部收合和全部展開。快速查詢會反白顯示並選取。第二個功能表會反白顯示,並提供快速查詢選項 評估、定義和評估、定義新函數,以及定義此模型中的所有函數。

TMDL 視圖中,您可以將函數 拖放到畫布中,或在模型總管中 UDF 的右鍵菜單中使用 Script TMDL 來生成腳本。

Power BI Desktop 中的模型總管窗格顯示展開的 [函數] 節點。兩個操作選單已開啟:第一個選單提供腳本至 TMDL、重新命名、從模型中刪除、在報表檢視中隱藏、全部取消隱藏、全部收合和全部展開。「腳本至 TMDL」被反白並選取。第二個選單被反白,並提供「腳本至 TMDL」選項,包括腳本標籤和剪貼簿。

使用 DMV 來檢查 UDF

您可以使用 動態管理檢視 (DMV) 來檢查模型中的 UDF。 這些檢視可讓您查詢函式的相關資訊,包括 UDF。

您可以使用 INFO.FUNCTIONS 函數來檢查模型中的 UDF。 欲將結果限制為僅 UDF,請將參數指定 ORIGIN2

EVALUATE INFO.FUNCTIONS("ORIGIN", "2")

此查詢會傳回模型中目前所有 UDF 的資料表,包括其名稱、描述和相關聯的中繼資料。

使用使用者定義的函數

定義 UDF 並儲存至模型之後,您可以從量值、計算欄、視覺化計算和其他 UDF 呼叫它。 這與呼叫內建 DAX 函數的運作方式相同。

在量值中呼叫 UDF

在量值中使用使用者自訂函數(UDF),以套用具有完整篩選內容的可重複使用邏輯。

Total Sales with Tax = AddTax ( [Total Sales] )

範例量值如下表所示:

顯示總銷售額和含稅總銷售額的表格。總銷售額(含稅)會反白顯示。視覺化窗格已開啟。含稅總銷售額在 欄 欄位井中反白顯示。

在計算欄位中呼叫 UDF

UDF 可以用來在計算欄中將可重複使用的邏輯套用至表格的每一列。

備註

在計算資料行中使用 UDF 時,請確定函式傳回類型一致的純量。 如需詳細資訊,請參閱 參數 。 如有需要,請使用 或類似函數將 CONVERT 結果轉換為所需的類型。

Sales Amount with Tax = CONVERT ( AddTax ( 'Sales'[Sales Amount] ), CURRENCY )

我們可以在下表中看到此範例量值:

顯示銷售金額和含稅銷售金額的表格。「含稅銷售金額」被高亮顯示。視覺化窗格已開啟。含稅銷售金額在欄位區域中被高亮顯示。

在視覺化計算中呼叫 UDF

您可以在視覺效果計算中使用 UDF,將邏輯直接套用至視覺效果。 如需視覺化計算的其他資訊,請參閱 視覺化計算

備註

視覺計算只會在視覺中存在的欄位上運作。 他們無法存取不屬於視覺效果的模型物件,而且您無法在此內容中將模型物件 (例如不在視覺效果中的欄或量值) 傳遞至 UDF。

Sales Amount with Tax = AddTax ( [Sales Amount] )

我們可以在下表中看到這個範例量值:

在視覺化計算編輯模式下。顯示銷售金額和銷售金額含稅的表格。「含稅銷售金額」會反白顯示。「含稅銷售額」的視覺化計算公式會反白顯示。

在另一個 UDF 中呼叫 UDF

您可以透過在一個函數中呼叫另一個函數來巢狀 UDF(使用者定義函數)。 在此範例中,我們定義簡單的 AddTax UDF 並在另一個 UDF AddTaxAndDiscount中呼叫它。

DEFINE
    /// AddTax takes in amount and returns amount including tax
    FUNCTION AddTax = 
        ( amount : NUMERIC ) =>
            amount * 1.1

	FUNCTION AddTaxAndDiscount = 
        (
			amount : NUMERIC,
			discount : NUMERIC
		) =>
		    AddTax ( amount - discount )

EVALUATE
{ AddTaxAndDiscount ( 10, 2 ) }
// Returns 8.8

參數

DAX UDF 可以接受零個或多個參數。 當您定義 UDF 的參數時,您可以選擇性地指定每個參數的類型提示:

  • 類型:參數接受的值類型 (AnyValScalarTableAnyRef)。
  • 子類型(僅適用於純量類型):特定的純量資料類型(VariantInt64DecimalDoubleStringDateTimeBoolean或)。Numeric
  • ParameterMode:在評估參數時使用(valexpr)。

類型提示的格式為: [type] [subtype] [parameterMode]

您可以為每個參數包含所有、部分或不包含這些類型提示,讓您的函數在呼叫站點更安全且更可預測。 如果您省略所有內容並只寫入參數名稱,則其行為為 AnyVal val,這意味著在呼叫時立即評估參數。 這對於簡單的函數很有用。

類型

類型定義參數接受的引數類別,以及它是以 還是 運算式的形式傳遞。

UDF 參數中有 DAX 兩個類型系列: 值類型運算式類型

  • 值類型:當呼叫函數並將結果值傳遞到函數中時,會 立即評估 此參數(預切評估)。
    • AnyVal:接受純量或表格。 如果您省略參數的類型,這是預設值。
    • Scalar:接受純量值 (可以額外新增子類型)。
    • Table:接受表格。
  • 運算式類型:此引數傳遞未評估的 運算式 (延遲評估)。 函數決定何時以及在什麼上下文中評估它。 這對於參照參數是必需的,當您需要控制過濾器上下文(例如在 CALCULATE 內)時非常有用。 expr 類型可以是欄、表格、行事曆或量值的參照。
    • AnyRef:接受一個參照(欄、表格、行事曆或量值)。

子類型

子型別可讓您定義特定的 Scalar 資料型別。 如果您定義子類型,則不需要明確將參數定義為 Scalar 類型,會自動假設此值。

亞型是:

  • Variant:接受任何純量。
  • Int64:接受整數。
  • Decimal:接受固定精確度的小數(例如,貨幣或金錢)。
  • Double:接受浮點小數。
  • String:接受文字。
  • DateTime:接受日期/時間。
  • Boolean:接受 TRUE/FALSE。
  • Numeric:接受任何數值 (Int64DecimalDouble 子類型)

參數模式

ParameterMode 會控制評估參數運算式的時間和位置。 這些包括:

  • val (eager evaluation):在呼叫函數之前,會先進行一次評估。 然後將結果值傳遞至函數。 這對於簡單的純量或表輸入很常見。 如果您省略參數的 parameterMode,則這是系統的預設值。
  • expr (延遲評估):表達式在函數內部進行評估,可能在不同的上下文(例如行上下文或過濾器上下文)中進行評估,如果多次引用或在迭代中引用,則可能會多次評估。 這是參考參數所必需的,當您需要控制評估內容時很有用。

Scalar 類型可以使用 valexpr。 當您想在呼叫端上下文中評估純量值時,請使用 val 。 當您想要延遲評估並可能在函數內套用內容時使用 expr 。 請參閱 範例:表格參數 作為範例。

類型 AnyRef 必須是 expr 因為它的參考(資料行、表格、量值等)需要在函數的內容中進行評估。

範例:類型轉換

DEFINE
    /// returns x cast to an Int64
    FUNCTION CastToInt = (
            x : SCALAR INT64 VAL
        ) =>
        x

EVALUATE
{ CastToInt ( 3.4 ), CastToInt ( 3.5 ), CastToInt ( "5" ) }
// returns 3, 4, 5

這會使用 Scalar type、Int64 subtype 和 val parameterMode 來進行可預測的舍入和文字轉換為數字的強制轉換,並確保任何運算式均被急迫地求值。 您也可以透過包含 Int64 子類型來達成此目的,如下例所示。 非數值字串會導致錯誤。

DEFINE
    /// returns x as an Int64
    FUNCTION CastToInt = (
            x : INT64
        ) =>
        x

EVALUATE
{ CastToInt ( 3.4 ), CastToInt ( 3.5 ), CastToInt ( "5" ) }
// returns 3, 4, 5

範例:表格參數 (值與運算式)

若要說明 UDF parameterMode 如何影響篩選內容,請考慮兩個函式,它們都會計算 'Sales' 資料表中的資料列。 兩者都在其內文中使用 CALCULATETABLE(t, ALL('Date')),但其中一個參數被聲明為 val(急切評估),而另一個參數被聲明為 expr(延遲評估):

DEFINE
    /// Table val: receives a materialized table, context can't be changed
    FUNCTION CountRowsNow = (
            t : TABLE VAL
        ) =>
        COUNTROWS ( CALCULATETABLE ( t, ALL ( 'Date' ) ) )
    
    /// Table expr: receives an unevaluated expression, context CAN be changed
    FUNCTION CountRowsLater = (
            t : TABLE EXPR
        ) =>
        COUNTROWS ( CALCULATETABLE ( t, ALL ( 'Date' ) ) )

EVALUATE
{
    CALCULATE ( CountRowsNow ( 'Sales' ), 'Date'[Fiscal Year] = "FY2020" ),
    CALCULATE ( CountRowsLater ( 'Sales' ), 'Date'[Fiscal Year] = "FY2020" )
}
// returns 84285, 121253

CountRowsNow 只會傳回 2020 財年的銷售計數。 'Sales' 表格在進入函數之前已經依年份篩選過,因此 ALL('Date') 在函數內沒有任何影響。

CountRowsLater 會傳回所有年份的銷售計數。 函式會接收未評估的資料表運算式,並在 ALL('Date') 下評估它,移除外部年份篩選器。

類型檢查

UDF 中的類型檢查可以使用新的和現有的類型檢查函數來完成,您可以在函數主體中調用以確認傳遞參數的運行時類型。 這允許 UDF 使用上下文控制、預先驗證參數、在計算之前標準化輸入。

備註

針對 expr parameterMode 參數,當參數在函式主體中參考時,會發生類型檢查 (而不是在函式呼叫時) 。

可用的類型檢查函式

UDF 可以使用下列函式來測試純量值。 每個傳回取決於 TRUE/FALSE 提供的值是否屬於該類型。

類別 Functions
Numeric ISNUMERICISNUMBER
Double ISDOUBLE
整數 ISINT64ISINTEGER
Decimal ISDECIMALISCURRENCY
繩子 ISSTRINGISTEXT
布林值 ISBOOLEANISLOGICAL
日期和時間 ISDATETIME

範例:檢查參數是否為字串

DEFINE
    /// Returns the length of a string, or BLANK if not a string
    FUNCTION StringLength = (
            s
        ) =>
        IF ( ISSTRING ( s ), LEN ( s ), BLANK () )

EVALUATE
{ StringLength ( "hello" ), StringLength ( 123 ) }
// Returns: 5, BLANK

這可防止錯誤,並可讓您決定如何處理函數中的非字串輸入 (在此範例中,傳回 BLANK)。

範例:接受多個參數類型

DEFINE
    /// Helper 1: get currency name by int64 key
    FUNCTION GetCurrencyNameByKey = (
            k : INT64
        ) =>
        LOOKUPVALUE ( 'Currency'[Currency], 'Currency'[CurrencyKey], k )
    
    /// Helper 2: get currency name by string code
    FUNCTION GetCurrencyNameByCode = (
            code : STRING
        ) =>
        LOOKUPVALUE ( 'Currency'[Currency], 'Currency'[Code], code )
    
    /// Accepts key (int64) or code (string) and returns the currency name
    FUNCTION GetCurrencyName = (
            currency
        ) =>
        IF (
            ISINT64 ( currency ),
            GetCurrencyNameByKey ( currency ),
            GetCurrencyNameByCode ( currency )
        )

EVALUATE
{ GetCurrencyName ( 36 ), GetCurrencyName ( "USD" ) }
// returns "Euro", "US Dollar"

此範例示範如何在 UDF 中使用類型檢查來安全地接受多個輸入類型,並傳回單一可預測的結果。 GetCurrencyName 採用一個引數, currency它可以是整數貨幣索引鍵或文字貨幣代碼。 函數使用ISINT64檢查引數類型。 如果輸入是整數,它會呼叫協助程式 GetCurrencyNameByKey ,以根據貨幣索引鍵查詢貨幣名稱。 如果輸入不是整數,它會呼叫協助程式 GetCurrencyNameByCode ,以根據貨幣代碼來查詢貨幣名稱。

一次定義多個函數

UDF 可讓您在單一查詢或腳本中定義多個函數,從而輕鬆組織可重複使用的邏輯。 當您想要將相關計算或協助程式常式封裝在一起時,這特別有用。 功能可以一起或單獨評估。

DEFINE
    /// Multiplies two numbers
    FUNCTION Multiply = (
            a,
            b
        ) =>
        a * b

    /// Adds two numbers and 1
    FUNCTION AddOne = (
            x,
            y
        ) =>
        x + y + 1

    /// Returns a random integer between 10 and 100
    FUNCTION RandomInt = () =>
        RANDBETWEEN ( 10, 100 )

EVALUATE
{ Multiply ( 3, 5 ), AddOne ( 1, 2 ), RandomInt () }
// returns 15, 4, 98

進階範例:彈性貨幣轉換

為了展示 UDF 如何 DAX 處理更複雜的邏輯,我們將研究貨幣轉換場景。 此範例使用類型檢查和巢狀函數,使用指定日期的平均或日終匯率將指定金額轉換為目標貨幣。

createOrReplace
	function ConvertDateToDateKey =  
		( 
			pDate: scalar variant 
		) => 
		YEAR ( pDate ) * 10000 + MONTH ( pDate ) * 100 + DAY ( pDate ) 
	
	function ConvertToCurrency = 
		( 
			pCurrency:scalar variant, 
			pDate: scalar variant, 
			pUseAverageRate: scalar boolean, 
			pAmount: scalar decimal 
		) => 
		var CurrencyKey = 
			EVALUATEANDLOG ( 
				IF ( 
					ISINT64 ( pCurrency ), 
					pCurrency, 
					CALCULATE ( 
						MAX ( 'Currency'[CurrencyKey] ), 
						'Currency'[Code] == pCurrency 
					) 
				) 
				, "CurrencyKey" 
			) 

		var DateKey = 
			EVALUATEANDLOG ( 
				SWITCH ( 
					TRUE, 
					ISINT64 ( pDate ), pDate, 
					ConvertDateToDateKey ( pDate ) 
				) 
				, "DateKey" 
			) 

		var ExchangeRate = 
			EVALUATEANDLOG ( 
				IF ( 
					pUseAverageRate, 
					CALCULATE ( 
						MAX ( 'Currency Rate'[Average Rate] ), 
						'Currency Rate'[DateKey] == DateKey, 
						'Currency Rate'[CurrencyKey] == CurrencyKey 
					), 
					CALCULATE ( 
					MAX ( 'Currency Rate'[End Of Day Rate] ), 
						'Currency Rate'[DateKey] == DateKey, 
						'Currency Rate'[CurrencyKey] == CurrencyKey 
					) 
				) 
				, "ExchangeRate" 
			) 

		var Result = 
			IF ( 
				ISBLANK ( pCurrency ) || ISBLANK ( pDate ) || ISBLANK ( pAmount ), 
				BLANK (), 
				IF ( 
					ISBLANK ( ExchangeRate ) , 
					"no exchange rate available", 
					ExchangeRate * pAmount 
				) 
			) 

		RETURN Result

ConvertToCurrency 函數接受貨幣和日期的彈性輸入類型。 使用者可以直接提供貨幣索引鍵或日期索引鍵,或提供貨幣代碼或標準日期值。 函數會檢查每個輸入的類型,並據此處理:如果是整數,則 pCurrency 視為貨幣索引鍵;否則,函數會假設貨幣代碼,並嘗試解析對應的索引鍵。 pDate 遵循類似的模式,如果是整數,則將其視為日期鍵;否則,函式會假設它是標準日期值,並使用協助程式函式轉換 ConvertDateToDateKey 成日期索引鍵。 如果函數無法確定有效的匯率,則會傳回訊息「沒有可用的匯率」。

然後,此邏輯可用來定義量值,例如以 當地貨幣表示的總銷售額

Total Sales in Local Currency = 
ConvertToCurrency (
    SELECTEDVALUE ( 'Currency'[Code] ),
    SELECTEDVALUE ( 'Date'[DateKey] ),
    TRUE,
    [Total Sales]
)

您可以選擇性地與 動態格式字串 配對,以適當的貨幣格式顯示結果。

CALCULATE (
    MAX ( 'Currency'[Format String] ),
    'Currency'[Code] == SELECTEDVALUE ( 'Currency'[Code] )
)

範例結果可以在下面的螢幕截圖中看到。

表格顯示完整日期、貨幣、當地貨幣中的總銷售額和總銷售額。

考慮事項與限制條件

使用者定義函式目前處於預覽狀態,在預覽期間,請注意下列考量和限制:

一般:

  • 無法在服務中編寫或建立 UDF 模型 DAX 。

  • 無法隱藏/取消隱藏模型中的 UDF。

  • 無法將 UDF 放入顯示資料夾中。

  • 功能區中沒有“創建功能”按鈕。

  • 無法將 UDF 與翻譯結合。

  • 沒有表格的模型不支援 UDF。

  • 查詢檢視中沒有「用參考資料定義」的快速查詢 DAX 。

  • Object-Level 安全(OLS) 不會轉移到函數,反之亦然。 例如,考慮以下函數 F,該函數指涉安全措施 MyMeasure

    function F = () => [MyMeasure] + 42
    

    當 以 MyMeasure 物件層級安全保護時,函式 F 不會自動被保護。 如果 F 在沒有存取 的 MyMeasure身份下執行,則會當作 MyMeasure 不存在。 我們建議避免在函式名稱和描述中揭露安全物件。

定義 UDF:

  • 不支援遞迴或相互遞迴。
  • 不支援函數多載。
  • 不支援明確傳回類型。

UDF 參數:

  • 不支援選擇性參數。
  • 不支援參數描述。
  • UDF 無法傳回值 enum 。 接受 enum 值作為其函數參數的內建函數將無法在該內容中使用 UDF。
  • 不會評估 hint expr 類型的未繫結參數。

IntelliSense 支援:

  • 雖然 UDF 可用於即時連線或複合模型,但沒有 IntelliSense 支援。
  • 雖然 UDF 可用於視覺化計算,但視覺化計算公式列沒有 UDF 的 IntelliSense 支援。
  • TMDL 檢視不支援 UDF 的完整 IntelliSense 功能。

已知的 Bug

目前已知下列問題,可能會影響功能:

  • UDF 中對表格式模型物件 (例如量值、表格、欄) 的參考在重新命名這些物件時不會自動更新。 如果您重新命名 UDF 相依的物件,函數主體仍會包含舊名稱。 您必須手動編輯 UDF 運算式,才能更新已重新命名物件的所有參考。
  • 某些涉及 UDF 的進階案例可能會導致剖析器不一致。 例如,使用者在將資料行作為expr參數傳遞或使用不合格的資料行參照時,可能會看到紅色底線或驗證錯誤。