Door de gebruiker gedefinieerde functies

Door de gebruiker gedefinieerde functies zijn herbruikbare subquery's die kunnen worden gedefinieerd als onderdeel van de query zelf (query-gedefinieerde functies) of die kunnen worden opgeslagen als onderdeel van de metagegevens van de database (opgeslagen functies). Door de gebruiker gedefinieerde functies worden aangeroepen via een naam, worden voorzien van nul of meer invoerargumenten (die scalair of tabellair kunnen zijn) en produceren één waarde (die scalair of tabellair kan zijn) op basis van de hoofdtekst van de functie.

Een door de gebruiker gedefinieerde functie behoort tot een van de volgende twee categorieën:

  • Scalaire functies
  • Tabellaire functies

De invoerargumenten en uitvoer van de functie bepalen of de functie scalair of tabellair is, waarmee wordt vastgesteld hoe de functie kan worden gebruikt.

Zie Query's optimaliseren die gebruikmaken van benoemde expressies als u meerdere toepassingen van de door de gebruiker gedefinieerde functies binnen één query wilt optimaliseren.

Scalaire functie

  • Heeft geen invoerargumenten of alle invoerargumenten zijn scalaire waarden
  • Produceert één scalaire waarde
  • Kan overal worden gebruikt waar een scalaire expressie is toegestaan
  • Mag alleen de rijcontext gebruiken waarin deze is gedefinieerd
  • Kan alleen verwijzen naar tabellen (en weergaven) die zich in het toegankelijke schema bevinden

Tabellaire functie

  • Accepteert een of meer tabellaire invoerargumenten en nul of meer scalaire invoerargumenten, en/of:
  • Produceert één tabellaire waarde

Functienamen

Geldige door de gebruiker gedefinieerde functienamen moeten voldoen aan dezelfde id-naamgevingsregels als andere entiteiten.

De naam moet ook uniek zijn in het bereik van de definitie.

Notitie

Als een opgeslagen functie en een tabel beide dezelfde naam hebben, wordt elke verwijzing naar die naam omgezet in de opgeslagen functie, niet de tabelnaam. Gebruik in plaats daarvan de tabelfunctie om naar de tabel te verwijzen.

Invoerargumenten

Geldige door de gebruiker gedefinieerde functies volgen deze regels:

  • Een door de gebruiker gedefinieerde functie heeft een sterk getypte lijst met nul of meer invoerargumenten.
  • Een invoerargument heeft een naam, een type en (voor scalaire argumenten) een standaardwaarde.
  • De naam van een invoerargument is een id.
  • Het type van een invoerargument is een van de scalaire gegevenstypen of een tabellair schema.

Syntactisch is de lijst met invoerargumenten een door komma's gescheiden lijst met argumentdefinities, tussen haakjes. Elke argumentdefinitie wordt opgegeven als

ArgName:ArgType [= ArgDefaultValue]

Voor tabelargumenten heeft ArgType dezelfde syntaxis als de tabeldefinitie (haakje en een lijst met kolomnaam-/typeparen), waarbij een solitair (*) wordt toegevoegd die 'elk tabellair schema' aangeeft.

Bijvoorbeeld:

Syntax Beschrijving van lijst met invoerargumenten
() Geen argumenten
(s:string) Eén scalair argument aangeroepen s om een waarde van het type te nemen string
(a:long, b:bool=true) Twee scalaire argumenten, waarvan de tweede een standaardwaarde heeft
(T1:(*), T2(r:real), b:bool) Drie argumenten (twee tabelargumenten en één scalair argument)

Notitie

Wanneer u zowel tabellaire invoerargumenten als scalaire invoerargumenten gebruikt, plaatst u alle tabellaire invoerargumenten vóór de scalaire invoerargumenten.

Voorbeelden

Scalaire functie

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

Tabellaire functie zonder argumenten

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

Tabellaire functie met argumenten

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

Uitvoer

x
9
10

Een tabellaire functie die gebruikmaakt van tabellaire invoer zonder kolom opgegeven. Elke tabel kan worden doorgegeven aan een functie en er kan in de functie niet naar tabelkolommen worden verwezen.

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

Uitvoer

x
1
2
3

Door de gebruiker gedefinieerde functies declareren

De declaratie van een door de gebruiker gedefinieerde functie biedt het volgende:

  • Functienaam
  • Functieschema (parameters die worden geaccepteerd, indien aanwezig)
  • Hoofdtekst van functie

Notitie

Overbelastingsfuncties worden niet ondersteund. U kunt niet meerdere functies maken met dezelfde naam en verschillende invoerschema's.

Tip

Lambda-functies hebben geen naam en zijn gebonden aan een naam met behulp van een let-instructie. Daarom kunnen ze worden beschouwd als door de gebruiker gedefinieerde opgeslagen functies. Voorbeeld: Declaratie voor een lambda-functie die twee argumenten accepteert (een aangeroepen strings en een long met de naam i). Het retourneert het product van de eerste (na conversie in een getal) en de tweede. De lambda is gebonden aan de naam f:

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

De hoofdtekst van de functie bevat:

  • Precies één expressie, die de retourwaarde van de functie (scalaire of tabellaire waarde) levert.
  • Een willekeurig aantal (nul of meer) let-instructies, waarvan het bereik dat is van de hoofdtekst van de functie. Indien opgegeven, moeten de let-instructies voorafgaan aan de expressie die de retourwaarde van de functie definieert.
  • Een willekeurig aantal (nul of meer) queryparametersinstructies, waarmee queryparameters worden declareren die door de functie worden gebruikt. Indien opgegeven, moeten deze voorafgaan aan de expressie die de retourwaarde van de functie definieert.

Notitie

Andere soorten queryinstructies die worden ondersteund op het 'hoogste niveau' van de query, worden niet ondersteund in de hoofdtekst van een functie. Twee instructies moeten worden gescheiden door een puntkomma.

Voorbeelden van door de gebruiker gedefinieerde functies

In de volgende sectie ziet u voorbeelden van het gebruik van door de gebruiker gedefinieerde functies.

Door de gebruiker gedefinieerde functie die gebruikmaakt van een let-instructie

In het volgende voorbeeld ziet u een door de gebruiker gedefinieerde functie (lambda) die een parameter met de naam ID accepteert. De functie is gebonden aan de naam Test en maakt gebruik van drie let-instructies , waarbij de definitie van Test3 de parameter ID gebruikt. Wanneer de query wordt uitgevoerd, is de uitvoer 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

Door de gebruiker gedefinieerde functie waarmee een standaardwaarde voor een parameter wordt gedefinieerd

In het volgende voorbeeld ziet u een functie die drie argumenten accepteert. De laatste twee hebben een standaardwaarde en hoeven niet aanwezig te zijn op de aanroepsite.

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"

Een door de gebruiker gedefinieerde functie aanroepen

De methode voor het aanroepen van een door de gebruiker gedefinieerde functie is afhankelijk van de argumenten die de functie verwacht te ontvangen. In de volgende secties wordt beschreven hoe u een UDF zonder argumenten aanroept, een UDF met scalaire argumenten aanroept en een UDF met tabelargumenten aanroept.

Een UDF zonder argumenten aanroepen

Een door de gebruiker gedefinieerde functie die geen argumenten heeft en kan worden aangeroepen door de naam of door de naam en een lege argumentenlijst tussen haakjes.

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

Een UDF aanroepen met scalaire argumenten

Een door de gebruiker gedefinieerde functie die een of meer scalaire argumenten gebruikt, kan worden aangeroepen met behulp van de functienaam en een lijst met concrete argumenten tussen haakjes:

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

Een UDF met tabelargumenten aanroepen

Een door de gebruiker gedefinieerde functie die een of meer tabelargumenten gebruikt (met een willekeurig aantal scalaire argumenten) en kan worden aangeroepen met behulp van de functienaam en een concrete lijst met argumenten tussen haakjes:

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

U kunt de operator invoke ook gebruiken om een door de gebruiker gedefinieerde functie aan te roepen die een of meer tabelargumenten gebruikt en een tabel retourneert. Deze functie is handig wanneer het eerste concrete tabelargument voor de functie de bron is van de invoke operator:

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

Standaardwaarden

Functies kunnen onder de volgende omstandigheden standaardwaarden opgeven voor sommige van hun parameters:

  • Standaardwaarden kunnen alleen worden opgegeven voor scalaire parameters.
  • Standaardwaarden zijn altijd letterlijke waarden (constanten). Het kunnen geen willekeurige berekeningen zijn.
  • Parameters zonder standaardwaarde gaan altijd vooraf aan parameters die wel een standaardwaarde hebben.
  • Aanroepers moeten de waarde van alle parameters opgeven zonder standaardwaarden die in dezelfde volgorde zijn gerangschikt als de functiedeclaratie.
  • Bellers hoeven de waarde voor parameters met standaardwaarden niet op te geven, maar kunnen dit wel doen.
  • Bellers kunnen argumenten opgeven in een volgorde die niet overeenkomt met de volgorde van de parameters. Zo ja, dan moeten ze hun argumenten een naam geven.

In het volgende voorbeeld wordt een tabel met twee identieke records geretourneerd. In de eerste aanroep van fzijn de argumenten volledig 'vervormd', zodat elke argumenten expliciet een naam krijgt:

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"

Uitvoer

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

Weergavefuncties

Een door de gebruiker gedefinieerde functie die geen argumenten gebruikt en een tabellaire expressie retourneert, kan worden gemarkeerd als een weergave. Het markeren van een door de gebruiker gedefinieerde functie als een weergave betekent dat de functie zich gedraagt als een tabel wanneer een naamomzetting met jokertekens wordt uitgevoerd.

In het volgende voorbeeld ziet u twee door de gebruiker gedefinieerde functies, T_view en T_notview, en ziet u hoe alleen de eerste wordt omgezet door de jokertekenverwijzing in de union:

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

Beperkingen

De volgende beperkingen zijn van toepassing:

  • Door de gebruiker gedefinieerde functies kunnen geen aanroepgegevens van toscalar() doorgeven die afhankelijk zijn van de rijcontext waarin de functie wordt aangeroepen.
  • Door de gebruiker gedefinieerde functies die een tabellaire expressie retourneren, kunnen niet worden aangeroepen met een argument dat afhankelijk is van de rijcontext.
  • Een functie die ten minste één tabellaire invoer gebruikt, kan niet worden aangeroepen op een extern cluster.
  • Een scalaire functie kan niet worden aangeroepen op een extern cluster.

De enige plaats waar een door de gebruiker gedefinieerde functie kan worden aangeroepen met een argument dat varieert met de rijcontext, is wanneer de door de gebruiker gedefinieerde functie alleen uit scalaire functies bestaat en geen gebruik maakt toscalar()van .

Voorbeelden

Ondersteunde scalaire functie

De volgende query wordt ondersteund omdat f het een scalaire functie is die niet verwijst naar een tabellaire expressie.

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)

De volgende query wordt ondersteund omdat f het een scalaire functie is die verwijst naar de tabellaire expressie Table1 , maar wordt aangeroepen zonder verwijzing naar de huidige rijcontext 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)

Niet-ondersteunde scalaire functie

De volgende query wordt niet ondersteund omdat f het een scalaire functie is die verwijst naar de tabellaire expressie Table1en wordt aangeroepen met een verwijzing naar de huidige rijcontext 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)

Niet-ondersteunde tabellaire functie

De volgende query wordt niet ondersteund omdat f het een tabellaire functie is die wordt aangeroepen in een context waarin een scalaire waarde wordt verwacht.

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)

Functies die momenteel niet worden ondersteund door door de gebruiker gedefinieerde functies

Voor de volledigheid zijn hier enkele veelgebruikte functies voor door de gebruiker gedefinieerde functies die momenteel niet worden ondersteund:

  1. Overbelasting van functies: Er is momenteel geen manier om een functie te overbelasten (een manier om meerdere functies met dezelfde naam en een ander invoerschema te maken).

  2. Standaardwaarden: de standaardwaarde voor een scalaire parameter voor een functie moet een scalaire letterlijke waarde (constante) zijn.