備註
DAX 使用者定義的函式目前處於 預覽階段。
DAX 使用者定義函數 (UDF) 可讓您封裝 DAX 邏輯並像任何其他 DAX 函數一樣重複使用它。 UDF 引進了新的 FUNCTION 關鍵字、選擇性 參數 (純量、表格和參考) 和 類型檢查 協助程式,讓編寫更安全、更清晰。
定義 UDF 之後,您可以在量值、計算資料行、視覺化計算,甚至其他使用者定義函數中使用它。 使用者可以集中業務規則、提高可維護性並隨著時間的推移安全地發展計算。 函式是您可以在查詢檢視和 DAX中建立和管理的第一類模型物件,而且可以在 [函式] 節點下的 [模型總管] 中檢視。
啟用使用者定義的功能
若要在桌面中試用 UDF:
- 前往 檔案 > 選項及設定 > 選項。
- 選取 預覽功能 並檢查 DAX 使用者定義的功能。
- 選取 [ 確定 ], 然後重新啟動 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。
使用 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 專案中使用 TMDL 指令碼
使用 Power BI 專案時,UDF 也會包含在語意模型 TMDL 腳本中。 它們可以在functions.tmdl定義資料夾中找到。
使用模型探索器
您可以從「 函數」 節點下的「模型總管」檢視模型中的所有使用者定義函數。 如需模型總管的其他資訊,請參閱 模型總管。
在查詢檢視中DAX,您可以在模型瀏覽器中透過 UDF 的滑鼠右鍵功能表中使用快速查詢,輕鬆定義和評估函數。
在 TMDL 視圖中,您可以將函數 拖放到畫布中,或在模型總管中 UDF 的右鍵菜單中使用 Script TMDL 來生成腳本。
使用 DMV 來檢查 UDF
您可以使用 動態管理檢視 (DMV) 來檢查模型中的 UDF。 這些檢視可讓您查詢函式的相關資訊,包括 UDF。
您可以使用 INFO.FUNCTIONS 函數來檢查模型中的 UDF。 欲將結果限制為僅 UDF,請將參數指定 ORIGIN 為 2。
EVALUATE INFO.FUNCTIONS("ORIGIN", "2")
此查詢會傳回模型中目前所有 UDF 的資料表,包括其名稱、描述和相關聯的中繼資料。
使用使用者定義的函數
定義 UDF 並儲存至模型之後,您可以從量值、計算欄、視覺化計算和其他 UDF 呼叫它。 這與呼叫內建 DAX 函數的運作方式相同。
在量值中呼叫 UDF
在量值中使用使用者自訂函數(UDF),以套用具有完整篩選內容的可重複使用邏輯。
Total Sales with Tax = AddTax ( [Total Sales] )
範例量值如下表所示:
在計算欄位中呼叫 UDF
UDF 可以用來在計算欄中將可重複使用的邏輯套用至表格的每一列。
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 的參數時,您可以選擇性地指定每個參數的類型提示:
-
類型:參數接受的值類型 (
AnyVal、Scalar、Table或AnyRef)。 -
子類型(僅適用於純量類型):特定的純量資料類型(
Variant、Int64DecimalDoubleStringDateTimeBoolean或)。Numeric -
ParameterMode:在評估參數時使用(
val或expr)。
類型提示的格式為: [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:接受任何數值 (Int64、Decimal或Double子類型)
參數模式
ParameterMode 會控制評估參數運算式的時間和位置。 這些包括:
-
val(eager evaluation):在呼叫函數之前,會先進行一次評估。 然後將結果值傳遞至函數。 這對於簡單的純量或表輸入很常見。 如果您省略參數的 parameterMode,則這是系統的預設值。 -
expr(延遲評估):表達式在函數內部進行評估,可能在不同的上下文(例如行上下文或過濾器上下文)中進行評估,如果多次引用或在迭代中引用,則可能會多次評估。 這是參考參數所必需的,當您需要控制評估內容時很有用。
Scalar 類型可以使用 val 或 expr。 當您想在呼叫端上下文中評估純量值時,請使用 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 | ISNUMERIC, ISNUMBER |
| Double | ISDOUBLE |
| 整數 | ISINT64,ISINTEGER |
| Decimal | ISDECIMAL,ISCURRENCY |
| 繩子 | 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參數傳遞或使用不合格的資料行參照時,可能會看到紅色底線或驗證錯誤。