Funkcje zdefiniowane przez użytkownika

Funkcje zdefiniowane przez użytkownika to podquery wielokrotnego użytku, które można zdefiniować jako część samego zapytania (funkcje zdefiniowane przez zapytanie) lub przechowywane jako część metadanych bazy danych (funkcje przechowywane). Funkcje zdefiniowane przez użytkownika są wywoływane za pomocą nazwy, są dostarczane z zerowymi lub większą większa większa liczba argumentów wejściowych (które mogą być skalarne lub tabelaryczne) i tworzą pojedynczą wartość (która może być skalarna lub tabelaryczna) na podstawie treści funkcji.

Funkcja zdefiniowana przez użytkownika należy do jednej z dwóch kategorii:

  • Funkcje skalarne
  • Funkcje tabelaryczne

Argumenty wejściowe i dane wyjściowe funkcji określają, czy jest skalarna, czy też tabelaryczna, co następnie określa sposób jej użycia.

Aby zoptymalizować wiele zastosowań funkcji zdefiniowanych przez użytkownika w ramach jednego zapytania, zobacz Optymalizowanie zapytań używających nazwanych wyrażeń.

Funkcja skalarna

  • Ma zerowe argumenty wejściowe lub wszystkie argumenty wejściowe są wartościami skalarnymi
  • Tworzy pojedynczą wartość skalarną
  • Można używać wszędzie tam, gdzie jest dozwolone wyrażenie skalarne
  • Może używać tylko kontekstu wiersza, w którym jest zdefiniowany
  • Może odwoływać się tylko do tabel (i widoków), które znajdują się w dostępnym schemacie

Funkcja tabelaryczna

  • Akceptuje co najmniej jeden argument wejściowy tabelaryczny oraz zero lub więcej argumentów wejściowych skalarnych i/lub:
  • Tworzy pojedynczą wartość tabelaryczny

Nazwy funkcji

Prawidłowe nazwy funkcji zdefiniowanych przez użytkownika muszą być zgodne z tymi samymi regułami nazewnictwa identyfikatorów co inne jednostki.

Nazwa musi być również unikatowa w zakresie definicji.

Uwaga

Jeśli funkcja przechowywana i tabela mają taką samą nazwę, wszelkie odwołania do tej nazwy są rozpoznawane dla przechowywanej funkcji, a nie nazwy tabeli. Zamiast tego użyj funkcji table , aby odwołać się do tabeli.

Argumenty wejściowe

Prawidłowe funkcje zdefiniowane przez użytkownika są zgodne z następującymi regułami:

  • Funkcja zdefiniowana przez użytkownika ma silnie typizowana listę zera lub większej liczby argumentów wejściowych.
  • Argument wejściowy ma nazwę, typ i (dla argumentów skalarnych) wartość domyślną.
  • Nazwa argumentu wejściowego jest identyfikatorem.
  • Typ argumentu wejściowego jest jednym z typów danych skalarnych lub schematem tabelarycznym.

Składniowo lista argumentów wejściowych jest rozdzielaną przecinkami listą definicji argumentów opakowaną w nawiasy. Każda definicja argumentu jest określona jako

ArgName:ArgType [= ArgDefaultValue]

W przypadku argumentów tabelarycznych typ ArgType ma taką samą składnię jak definicja tabeli (nawiasy i lista par nazw/typów kolumn), z dodaniem samotności (*) wskazującej "dowolny schemat tabelaryczny".

Na przykład:

Składnia Opis listy argumentów wejściowych
() Brak argumentów
(s:string) Pojedynczy argument skalarny o nazwie s biorąc wartość typu string
(a:long, b:bool=true) Dwa argumenty skalarne, z których druga ma wartość domyślną
(T1:(*), T2(r:real), b:bool) Trzy argumenty (dwa argumenty tabelaryczne i jeden argument skalarny)

Uwaga

W przypadku używania zarówno tabelarycznych argumentów wejściowych, jak i argumentów wejściowych skalarnych należy umieścić wszystkie argumenty wejściowe tabelaryczne przed argumentami wejściowymi skalarnymi.

Przykłady

Funkcja skalarna

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

Funkcja tabelaryczna bez argumentów

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

Funkcja tabelaryczna z argumentami

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

Dane wyjściowe

x
9
10

Funkcja tabelaryczna, która używa danych wejściowych tabelarycznych bez określonej kolumny. Każda tabela może zostać przekazana do funkcji i nie można odwoływać się do żadnych kolumn tabeli wewnątrz funkcji.

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

Dane wyjściowe

x
1
2
3

Deklarowanie funkcji zdefiniowanych przez użytkownika

Deklaracja funkcji zdefiniowanej przez użytkownika zapewnia:

  • Nazwa funkcji
  • Schemat funkcji (parametry, które akceptuje, jeśli istnieją)
  • Treść funkcji

Uwaga

Funkcje przeciążenia nie są obsługiwane. Nie można utworzyć wielu funkcji o tej samej nazwie i różnych schematach wejściowych.

Porada

Funkcje lambda nie mają nazwy i są powiązane z nazwą przy użyciu instrukcji let. W związku z tym można je traktować jako funkcje przechowywane zdefiniowane przez użytkownika. Przykład: Deklaracja dla funkcji lambda, która akceptuje dwa argumenty (wywoływane strings i nazywane i).long Zwraca produkt pierwszego (po przekonwertowaniu go na liczbę) i drugi. Lambda jest powiązana z nazwą f:

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

Treść funkcji obejmuje następujące elementy :

  • Dokładnie jedno wyrażenie, które udostępnia wartość zwracaną funkcji (wartość skalarna lub tabelaryczna).
  • Dowolna liczba (zero lub więcej) instrukcji let, których zakres jest zakresem treści funkcji. Jeśli zostanie określona, instrukcje let muszą poprzedzać wyrażenie definiujące wartość zwracaną funkcji.
  • Dowolna liczba (zero lub więcej) instrukcji parametrów zapytania, które deklarują parametry zapytania używane przez funkcję. Jeśli zostanie określony, muszą poprzedzać wyrażenie definiujące wartość zwracaną funkcji.

Uwaga

Inne rodzaje instrukcji zapytań obsługiwanych w zapytaniu "najwyższego poziomu" nie są obsługiwane w treści funkcji. Wszystkie dwie instrukcje muszą być oddzielone średnikiem.

Przykłady funkcji zdefiniowanych przez użytkownika

W poniższej sekcji przedstawiono przykłady używania funkcji zdefiniowanych przez użytkownika.

Funkcja zdefiniowana przez użytkownika, która używa instrukcji let

W poniższym przykładzie przedstawiono funkcję zdefiniowaną przez użytkownika (lambda), która akceptuje parametr o nazwie ID. Funkcja jest powiązana z nazwą Test i używa trzech instrukcji let , w których definicja Test3 używa parametru ID . Po uruchomieniu dane wyjściowe zapytania to 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

Funkcja zdefiniowana przez użytkownika, która definiuje wartość domyślną parametru

W poniższym przykładzie przedstawiono funkcję, która akceptuje trzy argumenty. Dwa ostatnie mają wartość domyślną i nie muszą być obecne w witrynie połączenia.

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"

Wywoływanie funkcji zdefiniowanej przez użytkownika

Metoda wywoływania funkcji zdefiniowanej przez użytkownika zależy od argumentów, których oczekuje funkcja. W poniższych sekcjach opisano sposób wywoływania funkcji UDF bez argumentów, wywoływania funkcji UDF z argumentami skalarnymi i wywoływaniafunkcji UDF z argumentami tabelarycznymi.

Wywoływanie funkcji UDF bez argumentów

Funkcja zdefiniowana przez użytkownika, która nie przyjmuje żadnych argumentów i może być wywoływana według jego nazwy lub nazwy oraz pustej listy argumentów w nawiasach.

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

Wywoływanie funkcji UDF z argumentami skalarnymi

Funkcja zdefiniowana przez użytkownika, która przyjmuje jeden lub więcej argumentów skalarnych, można wywołać przy użyciu nazwy funkcji i konkretnej listy argumentów w nawiasach:

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

Wywoływanie funkcji UDF z argumentami tabelarycznymi

Funkcja zdefiniowana przez użytkownika, która przyjmuje co najmniej jeden argument tabeli (z dowolną liczbą argumentów skalarnych) i może być wywoływana przy użyciu nazwy funkcji i konkretnej listy argumentów w nawiasach:

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

Możesz również użyć operatora invoke , aby wywołać funkcję zdefiniowaną przez użytkownika, która przyjmuje co najmniej jeden argument tabeli i zwraca tabelę. Ta funkcja jest przydatna, gdy pierwszy konkretny argument tabeli funkcji jest źródłem invoke operatora:

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

Wartości domyślne

Funkcje mogą udostępniać wartości domyślne niektórym z ich parametrów w następujących warunkach:

  • Wartości domyślne mogą być podane tylko dla parametrów skalarnych.
  • Wartości domyślne to zawsze literały (stałe). Nie mogą być to dowolne obliczenia.
  • Parametry bez wartości domyślnej zawsze poprzedzają parametry, które mają wartość domyślną.
  • Obiekt wywołujący musi podać wartość wszystkich parametrów bez wartości domyślnych rozmieszczonych w tej samej kolejności co deklaracja funkcji.
  • Osoby wywołujące nie muszą podawać wartości parametrów z wartościami domyślnymi, ale mogą to zrobić.
  • Obiekt wywołujący może podać argumenty w kolejności, która nie jest zgodna z kolejnością parametrów. Jeśli tak, muszą nazwać swoje argumenty.

Poniższy przykład zwraca tabelę z dwoma identycznymi rekordami. W pierwszym wywołaniu fargumenty są całkowicie "scrambled", więc każda z nich jest jawnie podana nazwa:

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"

Dane wyjściowe

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

Wyświetlanie funkcji

Funkcja zdefiniowana przez użytkownika, która nie przyjmuje argumentów i zwraca wyrażenie tabelaryczne, można oznaczyć jako widok. Oznaczanie funkcji zdefiniowanej przez użytkownika jako widoku oznacza, że funkcja zachowuje się jak tabela przy każdym wykonaniu rozpoznawania nazw tabel z symbolami wieloznacznymi.

W poniższym przykładzie przedstawiono dwie funkcje zdefiniowane przez użytkownika i T_notview, i pokazano, T_view jak tylko pierwszy z nich jest rozpoznawany przez odwołanie wieloznaczne w elemencie union:

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

Ograniczenia

Obowiązują następujące ograniczenia:

  • Funkcje zdefiniowane przez użytkownika nie mogą przekazywać do informacji wywołania toscalar(), które zależą od kontekstu wiersza, w którym jest wywoływana funkcja.
  • Funkcje zdefiniowane przez użytkownika, które zwracają wyrażenie tabelaryczne, nie można wywołać z argumentem, który różni się od kontekstu wiersza.
  • Funkcja, która przyjmuje co najmniej jedno dane wejściowe tabelaryczne, nie może być wywoływana w klastrze zdalnym.
  • Nie można wywołać funkcji skalarnej w klastrze zdalnym.

Jedyną funkcją zdefiniowaną przez użytkownika może być wywołana argument, który różni się w zależności od kontekstu wiersza, jest to, gdy funkcja zdefiniowana przez użytkownika składa się tylko z funkcji skalarnych i nie używa toscalar()metody .

Przykłady

Obsługiwana funkcja skalarna

Poniższe zapytanie jest obsługiwane, ponieważ f jest funkcją skalarną, która nie odwołuje się do żadnego wyrażenia tabelarycznego.

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)

Następujące zapytanie jest obsługiwane, ponieważ f jest funkcją skalarną, która odwołuje się do wyrażenia Table1 tabelarycznego, ale jest wywoływana bez odwołania do bieżącego kontekstu f(10)wiersza:

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)

Nieobsługiwana funkcja skalarna

Następujące zapytanie nie jest obsługiwane, ponieważ f jest funkcją skalarną, która odwołuje się do wyrażenia Table1tabelarycznego , i jest wywoływana z odwołaniem do bieżącego kontekstu f(Column)wiersza:

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)

Nieobsługiwana funkcja tabelaryczna

Następujące zapytanie nie jest obsługiwane, ponieważ f jest to funkcja tabelaryczna wywoływana w kontekście, który oczekuje wartości skalarnej.

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)

Funkcje, które są obecnie nieobsługiwane przez funkcje zdefiniowane przez użytkownika

Aby uzyskać kompletność, poniżej przedstawiono niektóre często wymagane funkcje dla funkcji zdefiniowanych przez użytkownika, które nie są obecnie obsługiwane:

  1. Przeciążenie funkcji: obecnie nie ma możliwości przeciążenia funkcji (sposób tworzenia wielu funkcji o tej samej nazwie i innym schemacie wejściowym).

  2. Wartości domyślne: wartość domyślna parametru skalarnego dla funkcji musi być literałem skalarnym (stała).