Användardefinierade funktioner

Användardefinierade funktioner är återanvändbara underfrågor som kan definieras som en del av själva frågan (frågedefinierade funktioner) eller lagras som en del av databasens metadata (lagrade funktioner). Användardefinierade funktioner anropas via ett namn, tillhandahålls med noll eller flera indataargument (som kan vara skalära eller tabellbaserade) och genererar ett enda värde (som kan vara skalärt eller tabellbaserat) baserat på funktionstexten.

En användardefinierad funktion tillhör en av två kategorier:

  • Skalärfunktioner
  • Tabellfunktioner

Funktionens indataargument och utdata avgör om den är skalär eller tabulär, vilket sedan fastslår hur den kan användas.

Information om hur du optimerar flera användningsområden för användardefinierade funktioner i en enskild fråga finns i Optimera frågor som använder namngivna uttryck.

Skalär funktion

  • Har noll indataargument, eller så är alla indataargument skalära värden
  • Genererar ett enda skalärt värde
  • Kan användas oavsett var ett skalärt uttryck tillåts
  • Får endast använda den radkontext där den har definierats
  • Kan endast referera till tabeller (och vyer) som finns i det tillgängliga schemat

Tabellfunktion

  • Accepterar ett eller flera tabellindataargument och noll eller flera skalära indataargument och/eller:
  • Skapar ett enskilt tabellvärde

Funktionsnamn

Giltiga användardefinierade funktionsnamn måste följa samma namngivningsregler för identifierare som andra entiteter.

Namnet måste också vara unikt i definitionens omfattning.

Anteckning

Om både en lagrad funktion och en tabell har samma namn matchas alla referenser till det namnet till den lagrade funktionen, inte tabellnamnet. Använd tabellfunktionen för att referera till tabellen i stället.

Indataargument

Giltiga användardefinierade funktioner följer dessa regler:

  • En användardefinierad funktion har en starkt typifierad lista med noll eller flera indataargument.
  • Ett indataargument har ett namn, en typ och (för skalära argument) ett standardvärde.
  • Namnet på ett indataargument är en identifierare.
  • Typen av indataargument är antingen en av skalära datatyper eller ett tabellschema.

Syntaktiskt är listan med indataargument en kommaavgränsad lista med argumentdefinitioner, omslutna med parenteser. Varje argumentdefinition anges som

ArgName:ArgType [= ArgDefaultValue]

För tabellargument har ArgType samma syntax som tabelldefinitionen (parenteser och en lista över kolumnnamn/typpar), med tillägget av ett enstitet (*) som anger "valfritt tabellschema".

Exempel:

Syntax Listbeskrivning för indataargument
() Inga argument
(s:string) Enkelt skalärt argument som kallas s att ta ett värde av typen string
(a:long, b:bool=true) Två skalära argument, varav den andra har ett standardvärde
(T1:(*), T2(r:real), b:bool) Tre argument (två tabellargument och ett skalärt argument)

Anteckning

När du använder både tabellbaserade indataargument och skalära indataargument placerar du alla tabellindataargument före de skalära indataargumenten.

Exempel

Skalär funktion

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()

Tabellfunktion utan argument

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

Tabellfunktion med argument

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

Resultat

x
9
10

En tabellfunktion som använder tabellindata utan angiven kolumn. Alla tabeller kan skickas till en funktion och inga tabellkolumner kan refereras till i funktionen.

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

Resultat

x
1
2
3

Deklarera användardefinierade funktioner

Deklarationen av en användardefinierad funktion ger:

  • Funktionsnamn
  • Funktionsschema (parametrar som accepteras, om det finns)
  • Funktionstext

Anteckning

Överlagringsfunktioner stöds inte. Du kan inte skapa flera funktioner med samma namn och olika indatascheman.

Tips

Lambda-funktioner har inget namn och är bundna till ett namn med hjälp av en let-instruktion. Därför kan de betraktas som användardefinierade lagrade funktioner. Exempel: Deklaration för en lambda-funktion som accepterar två argument (en string anropad s och en long med namnet i). Den returnerar produkten av den första (efter att den konverterats till ett tal) och den andra. Lambda är bunden till namnet f:

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

Funktionstexten innehåller:

  • Exakt ett uttryck, som tillhandahåller funktionens returvärde (skalärt eller tabellvärde).
  • Valfritt tal (noll eller mer) av let-instruktioner, vars omfång är funktionens brödtext. Om detta anges måste let-uttrycken föregå uttrycket som definierar funktionens returvärde.
  • Valfritt tal (noll eller mer) av frågeparametrar, som deklarerar frågeparametrar som används av funktionen. Om det anges måste de föregå uttrycket som definierar funktionens returvärde.

Anteckning

Andra typer av frågeinstruktioner som stöds på frågan "översta nivån" stöds inte i en funktionstext. Två instruktioner måste avgränsas med semikolon.

Exempel på användardefinierade funktioner

I följande avsnitt visas exempel på hur du använder användardefinierade funktioner.

Användardefinierad funktion som använder en let-instruktion

I följande exempel visas en användardefinierad funktion (lambda) som accepterar en parameter med namnet ID. Funktionen är bunden till namnet Test och använder tre let-instruktioner , där Test3-definitionen använder ID-parametern . Vid körning är utdata från frågan 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

Användardefinierad funktion som definierar ett standardvärde för en parameter

I följande exempel visas en funktion som accepterar tre argument. De två sistnämnda har ett standardvärde och behöver inte finnas på anropsplatsen.

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"

Anropa en användardefinierad funktion

Metoden för att anropa en användardefinierad funktion beror på de argument som funktionen förväntar sig att ta emot. Följande avsnitt beskriver hur du anropar en UDF utan argument, anropar en UDF med skalära argument och anropar en UDF med tabellargument.

Anropa en UDF utan argument

En användardefinierad funktion som inte tar några argument och kan anropas antingen med dess namn eller av dess namn och en tom argumentlista inom parentes.

// 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())

Anropa en UDF med skalära argument

En användardefinierad funktion som tar ett eller flera skalära argument kan anropas med hjälp av funktionsnamnet och en konkret argumentlista inom parentes:

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

Anropa en UDF med tabellargument

En användardefinierad funktion som tar ett eller flera tabellargument (med valfritt antal skalära argument) och kan anropas med hjälp av funktionsnamnet och en konkret argumentlista inom parentes:

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

Du kan också använda operatorn invoke för att anropa en användardefinierad funktion som tar ett eller flera tabellargument och returnerar en tabell. Den här funktionen är användbar när det första konkreta tabellargumentet till funktionen är källan till operatorn 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(":-)")

Standardvärden

Funktioner kan ange standardvärden för vissa av sina parametrar under följande villkor:

  • Standardvärden kan endast anges för skalära parametrar.
  • Standardvärden är alltid literaler (konstanter). De kan inte vara godtyckliga beräkningar.
  • Parametrar utan standardvärde föregår alltid parametrar som har ett standardvärde.
  • Anropare måste ange värdet för alla parametrar utan standardvärden ordnade i samma ordning som funktionsdeklarationen.
  • Anropare behöver inte ange värdet för parametrar med standardvärden, men kan göra det.
  • Anropare kan ange argument i en ordning som inte matchar parametrarnas ordning. I så fall måste de namnge sina argument.

I följande exempel returneras en tabell med två identiska poster. I det första anropet av fär argumenten helt "förvrängda", så var och en får uttryckligen ett namn:

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"

Resultat

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

Visa funktioner

En användardefinierad funktion som inte tar några argument och returnerar ett tabelluttryck kan markeras som en vy. Om en användardefinierad funktion markeras som en vy innebär det att funktionen fungerar som en tabell när en matchning av namn på jokerteckentabeller utförs.

I följande exempel visas två användardefinierade funktioner, T_view och T_notview, och visar hur endast den första matchas med jokerteckenreferensen unioni :

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

Begränsningar

Följande begränsningar gäller:

  • Användardefinierade funktioner kan inte skicka in anropsinformation för toscalar() som är beroende av radkontexten där funktionen anropas.
  • Användardefinierade funktioner som returnerar ett tabelluttryck kan inte anropas med ett argument som varierar med radkontexten.
  • En funktion som tar minst en tabellindata kan inte anropas i ett fjärrkluster.
  • En skalär funktion kan inte anropas på ett fjärrkluster.

Den enda plats där en användardefinierad funktion kan anropas med ett argument som varierar med radkontexten är när den användardefinierade funktionen endast består av skalärfunktioner och inte använder toscalar().

Exempel

Skalära funktioner som stöds

Följande fråga stöds eftersom f är en skalär funktion som inte refererar till något tabelluttryck.

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öljande fråga stöds eftersom f är en skalär funktion som refererar till tabelluttrycket Table1 men som anropas utan referens till den aktuella radkontexten f(10):

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)

Skalära funktioner som inte stöds

Följande fråga stöds inte eftersom f är en skalär funktion som refererar till tabelluttrycket Table1och anropas med en referens till den aktuella radkontexten 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)

Tabellfunktion som inte stöds

Följande fråga stöds inte eftersom f är en tabellfunktion som anropas i en kontext som förväntar sig ett skalärt värde.

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)

Funktioner som för närvarande inte stöds av användardefinierade funktioner

Här följer några vanliga funktioner som efterfrågas för användardefinierade funktioner som för närvarande inte stöds:

  1. Funktionsöverlagring: Det finns för närvarande inget sätt att överbelasta en funktion (ett sätt att skapa flera funktioner med samma namn och olika indatascheman).

  2. Standardvärden: Standardvärdet för en skalär parameter för en funktion måste vara en skalär literal (konstant).