使用者定義的函式

使用者定義函 式是可重複使用的子查詢,可以定義為查詢本身的一部分, (查詢定義 函式) ,或儲存為 (預存 函式) 的一部分。 使用者定義的函式會透過名稱叫用、獲得零個或多個輸入引數 (可以是純量或表格式),並根據函式主體產生單一值 (可以是純量或表格式)。

使用者定義的函式屬於下列兩個類別之一:

  • 純量函式
  • 表格式函式

函數的輸入引數和輸出可決定其為純量或表格式,據以建立其使用方式。

若要在單一查詢中優化使用者定義函數的多個用法,請參閱 優化使用具名表達式的查詢

純量函數

  • 有零個輸入引數,或其所有輸入引數都是純量值
  • 產生單一純量值
  • 可以在允許使用純量運算式的情況下使用
  • 只能使用其中定義的數據列內容
  • 只能參考位於可存取結構描述中的資料表 (和檢視)

表格式函式

  • 接受一或多個表格式輸入引數,及零個或多個純量輸入引數,以及/或:
  • 產生單一表格式值

函式名稱

有效的使用者定義函式名稱必須遵循與其他實體相同的識別碼命名規則

名稱在其定義範圍中也必須是唯一的。

注意

如果預存函式和資料表都有相同的名稱,則該名稱的任何參考都會解析成預存函式,而非資料表名稱。 請改用資料表函式來參考資料表。

輸入引數

有效的使用者定義函式會遵循下列規則:

  • 用戶定義函數具有零個或多個輸入自變數的強型別清單。
  • 輸入引數具有名稱、類型和 (適用於純量引數) 預設值
  • 輸入引數的名稱是識別碼。
  • 輸入引數的類型是其中一種純量資料類型或表格式結構描述。

在語法上,輸入引數清單是以逗號分隔的引數定義清單 (以括號括住)。 每個引數定義都會指定為

ArgName:ArgType [= ArgDefaultValue]

對於表格式自變數, ArgType 的語法與數據表定義 (括弧和) 的數據行名稱/類型組清單相同,加上指出「任何表格式架構」的自述 (*)

例如:

語法 輸入引數清單描述
() 無引數
(s:string) 稱為 s 並可接受 string 類型值的單一純量引數
(a:long, b:bool=true) 兩個純量引數,第二個具有預設值
(T1:(*), T2(r:real), b:bool) 三個引數 (兩個表格式引數和一個純量引數)

注意

同時使用表格式輸入引數和純量輸入引數時,請將所有表格式輸入引數放在純量輸入引數之前。

範例

純量函數

let Add7 = (arg0:long = 5) { arg0 + 7 };
range x from 1 to 10 step 1
| extend x_plus_7 = Add7(x), five_plus_seven = Add7()

沒有自變數的表格式函式

let tenNumbers = () { range x from 1 to 10 step 1};
tenNumbers
| extend x_plus_7 = x + 7

具有自變數的表格式函式

let MyFilter = (T:(x:long), v:long) {
  T | where x >= v
};
MyFilter((range x from 1 to 10 step 1), 9)

輸出

x
9
10

表格式函式,其使用未指定資料行的表格式輸入。 任何資料表都可以傳遞至函式,而且不能在函式內參考任何資料表資料行。

let MyDistinct = (T:(*)) {
  T | distinct *
};
MyDistinct((range x from 1 to 3 step 1))

輸出

x
1
2
3

宣告使用者定義的函式

使用者定義函式的宣告可提供:

  • 函式名稱
  • 函式結構描述 (如果有的話,其接受的參數)
  • 函式主體

注意

不支援多載函式。 您無法建立多個具有相同名稱和不同輸入結構描述的函式。

提示

Lambda 函式沒有名稱,而且會使用 let 陳述式繫結至名稱。 因此,可以將其視為使用者定義的預存函式。 範例:Lambda 函式的宣告,可接受兩個引數 (稱為 sstring,以及稱為 ilong)。 其會傳回第一個引數 (將其轉換為數字之後) 與第二個引數的產物。 Lambda 會繫結至名稱 f

let f=(s:string, i:long) {
    tolong(s) * i
};

函式主體包含:

  • 正好一個運算式,其可提供函式的傳回值 (純量或表格式值)。
  • 任意數量 (零個或多個) 的 let 陳述式,其範圍是函式主體的範圍。 若已指定,let 陳述式必須在定義函式傳回值的運算式前面。
  • 任意數量 (零個或多個) 的query parameters 陳述式,其可宣告函式所使用的查詢參數。 若已指定,其必須在定義函式傳回值的運算式前面。

注意

在函式主體內,不支援查詢「最上層」所支援的其他種類查詢陳述式。 任何兩個陳述式均必須以分號分隔。

使用者定義的函式範例

下一節顯示如何使用使用者定義函數的範例。

使用 let 陳述式的使用者定義函式

下列範例示範用戶定義函數 (Lambda) ,可接受名為 ID 的參數。 函式系結至 Test 名稱,並使用三個 let 語句,其中 Test3 定義會使用 ID 參數。 執行時,查詢的輸出為 70:

let Test = (id: int) {
  let Test2 = 10;
  let Test3 = 10 + Test2 + id;
  let Test4 = (arg: int) {
      let Test5 = 20;
      Test2 + Test3 + Test5 + arg
  };
  Test4(10)
};
range x from 1 to Test(10) step 1
| count

可定義參數預設值的使用者定義函式

下列範例顯示可接受三個引數的函式。 後者有預設值,而且不需要出現在通話站臺上。

let f = (a:long, b:string = "b.default", c:long = 0) {
  strcat(a, "-", b, "-", c)
};
print f(12, c=7) // Returns "12-b.default-7"

叫用使用者定義的函式

叫用使用者定義函數的方法取決於函式預期接收的自變數。 下列各節說明如何 叫用不含自變數的 UDF使用純量自變數叫用 UDF,以及 使用表格式自變數叫用 UDF

叫用不含自變數的UDF

不採用任何自變數且可由其名稱叫用的使用者定義函式,或是依其名稱叫用,並在括號中叫用空的自變數清單。

// Bind the identifier a to a user-defined function (lambda) that takes
// no arguments and returns a constant of type long:
let a=(){123};
// Invoke the function in two equivalent ways:
range x from 1 to 10 step 1
| extend y = x * a, z = x * a()
// Bind the identifier T to a user-defined function (lambda) that takes
// no arguments and returns a random two-by-two table:
let T=(){
  range x from 1 to 2 step 1
  | project x1 = rand(), x2 = rand()
};
// Invoke the function in two equivalent ways:
// (Note that the second invocation must be itself wrapped in
// an additional set of parentheses, as the union operator
// differentiates between "plain" names and expressions)
union T, (T())

使用純量自變數叫用UDF

使用函數名稱和括號中的具象自變數清單,即可叫用採用一或多個純量自變數的使用者定義函數:

let f=(a:string, b:string) {
  strcat(a, " (la la la)", b)
};
print f("hello", "world")

使用表格式自變數叫用UDF

使用者定義函數,採用一或多個數據表自變數, (具有任意數目純量自變數) ,而且可以使用括弧中的函式名稱和具象自變數清單來叫用:

let MyFilter = (T:(x:long), v:long) {
  T | where x >= v
};
MyFilter((range x from 1 to 10 step 1), 9)

您也可以使用 invoke 運算子來叫用可接受一或多個資料表引數並傳回資料表的使用者定義函式。 當函式的第一個具體資料表引數是 invoke 運算子的來源時,此函式很有用:

let append_to_column_a=(T:(a:string), what:string) {
    T | extend a=strcat(a, " ", what)
};
datatable (a:string) ["sad", "really", "sad"]
| invoke append_to_column_a(":-)")

預設值

在下列情況下,函式可能會提供其某些參數的預設值:

  • 只會對純量參數提供預設值。
  • 預設值一律為常值 (常數)。 不能是任意計算。
  • 沒有預設值的參數一律在具有預設值的參數前面。
  • 呼叫端必須提供所有參數的值,且預設值不會以與函式宣告相同的順序排列。
  • 呼叫端不需要提供具有預設值的參數值,但可能會這麼做。
  • 呼叫端可能會以不符合參數順序的順序來提供引數。 若是如此,就必須為其引數命名。

下列範例會傳回具有兩筆相同記錄的資料表。 在 f 的第一次叫用中,引數會完全「變碼」,因此每一筆記錄都會有明確指定的名稱:

let f = (a:long, b:string = "b.default", c:long = 0) {
  strcat(a, "-", b, "-", c)
};
union
  (print x=f(c=7, a=12)), // "12-b.default-7"
  (print x=f(12, c=7))    // "12-b.default-7"

輸出

x
12-b.default-7
12-b.default-7

檢視函式

不接受引數並傳回表格式運算式的使用者定義函式,可以標示為 view。 將使用者定義函數標示為檢視,表示每當執行通配符數據表名稱解析時,函式的行為就像數據表一樣。

下列範例顯示兩個使用者定義的函式 (T_viewT_notview),並顯示 union 中的萬用字元參考如何只解析第一個函式:

let T_view = view () { print x=1 };
let T_notview = () { print x=2 };
union T*

限制

適用以下限制:

  • 使用者定義的函式不能傳入 toscalar() 叫用資訊,其取決於函式呼叫所在的資料列內容。
  • 不能使用隨著資料列內容變化的引數,叫用可傳回表格式運算式的使用者定義函式。
  • 無法在遠端叢集上叫用至少接受一個表格式輸入的函式。
  • 無法在遠端叢集上叫用純量函式。

只有當使用者定義的函式僅由純量函式組成且未使用 toscalar() 時,才能使用會隨著資料列內容變化的引數叫用使用者定義的函式。

範例

支援的純量函式

支援下列查詢,因為 f 是未參考任何表格式表達式的純量函數。

let Table1 = datatable(xdate:datetime)[datetime(1970-01-01)];
let Table2 = datatable(Column:long)[1235];
let f = (hours:long) { now() + hours*1h };
Table2 | where Column != 123 | project d = f(10)

支援下列查詢,因為 f 是參考表格式表示式但未參考目前數據列內容的f(10)純量函數Table1

let Table1 = datatable(xdate:datetime)[datetime(1970-01-01)];
let Table2 = datatable(Column:long)[1235];
let f = (hours:long) { toscalar(Table1 | summarize min(xdate) - hours*1h) };
Table2 | where Column != 123 | project d = f(10)

不支援的純量函式

不支援下列查詢,因為 f 是參考表格式表示式 的純量函數 Table1,而且會使用目前數據列內容的 f(Column)參考來叫用 :

let Table1 = datatable(xdate:datetime)[datetime(1970-01-01)];
let Table2 = datatable(Column:long)[1235];
let f = (hours:long) { toscalar(Table1 | summarize min(xdate) - hours*1h) };
Table2 | where Column != 123 | project d = f(Column)

不支援的表格式函數

不支援下列查詢,因為 f 是在預期純量值的內容中叫用的表格式函式。

let Table1 = datatable(xdate:datetime)[datetime(1970-01-01)];
let Table2 = datatable(Column:long)[1235];
let f = (hours:long) { range x from 1 to hours step 1 | summarize make_list(x) };
Table2 | where Column != 123 | project d = f(Column)

使用者定義的函式目前不支援的功能

為了完整性,以下是目前不支援之使用者定義函式的一些常用功能:

  1. 函式多載:目前沒有任何方法可以多載函式 (建立多個具有相同名稱和不同輸入架構的函式) 。

  2. 預設值:函式的純量參數預設值必須是純量常值 (常數)。