Функции, определённые пользователем

Переключайте службы с помощью раскрывающегося списка Версия. Дополнительные сведения о навигации.
Область применения: ✅ Microsoft Fabric ✅ Azure Data Explorer ✅ Azure Monitor ✅ Microsoft Sentinel

Определяемые пользователем функции — это повторно используемые вложенные запросы, которые могут быть определены как часть самого запроса (определяемые запросом функции) или хранятся в составе метаданных базы данных (хранимых функций). Определяемые пользователем функции вызываются с помощью имени, предоставляются с нуля или нескольких входных аргументов (которые могут быть скалярными или табличными) и создают одно значение (которое может быть скалярным или табличным) на основе текста функции.

Определяемая пользователем функция относится к одной из двух категорий:

  • Скалярные функции
  • Табличные функции

Входные аргументы и выходные данные функции определяют, является ли она скалярной или табличной, которая затем определяет, как она может использоваться.

Чтобы оптимизировать несколько пользовательских функций в одном запросе, см. статью "Оптимизация запросов", использующих именованные выражения.

Мы создали ассортимент определяемых пользователем функций, которые можно использовать в запросах. Дополнительные сведения см. в библиотеке функций.

Скалярная функция

  • Имеет нулевые входные аргументы или все входные аргументы являются скалярными значениями
  • Создает одно скалярное значение
  • Можно использовать везде, где разрешено скалярное выражение
  • Может использовать только контекст строки, в котором он определен
  • Может ссылаться только на таблицы (и представления), которые находятся в схеме со специальными возможностями

Табличная функция

  • Принимает один или несколько табличных входных аргументов, а также ноль или несколько скалярных входных аргументов и/или:
  • Создает одно табличное значение

Имена функций

Допустимые имена определяемых пользователем функций должны соответствовать тем же правилам именования идентификаторов , что и другие сущности.

Имя также должно быть уникальным в области определения.

Замечание

Если хранимая функция и таблица имеют одинаковое имя, то любая ссылка на это имя разрешается в хранимую функцию, а не имя таблицы. Используйте функцию таблицы , чтобы ссылаться на таблицу.

Входные аргументы

Допустимые определяемые пользователем функции следуют этим правилам:

  • Определяемая пользователем функция имеет строго типизированный список нулевых или более входных аргументов.
  • Входной аргумент имеет имя, тип и (для скалярных аргументов) значение по умолчанию.
  • Имя входного аргумента — это идентификатор.
  • Тип входного аргумента является одним из скалярных типов данных или табличной схемы.

Синтаксически список входных аргументов — это разделенный запятыми список определений аргументов, упакованный в скобки. Каждое определение аргумента указывается как

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

Объявление определяемых пользователем функций

Объявление определяемой пользователем функции обеспечивает:

  • Имя функции
  • Схема функции (параметры, которые он принимает, если таковые есть)
  • Тело функции

Замечание

Функции перегрузки не поддерживаются. Невозможно создать несколько функций с одинаковым именем и разными входными схемами.

Подсказка

Лямбда-функции не имеют имени и привязаны к имени с помощью инструкции let. Поэтому их можно рассматривать как определяемые пользователем хранимые функции. Пример: объявление лямбда-функции, которая принимает два аргумента ( string вызываемая s и вызываемая longi). Он возвращает продукт первого (после преобразования его в число) и второй. Лямбда-лямбда привязана к имени f:

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

Текст функции включает:

  • Ровно одно выражение, которое предоставляет возвращаемое значение функции (скалярное или табличное значение).
  • Любое число (ноль или больше) операторов let, область действия которого является частью тела функции. Если задано, операторы let должны предшествовать выражению, определяющему возвращаемое значение функции.
  • Любое число (ноль или больше) инструкций параметров запроса, которые объявляют параметры запроса, используемые функцией. Если задано, они должны предшествовать выражению, определяющему возвращаемое значение функции.

Замечание

Другие типы инструкций запросов , поддерживаемые на "верхнем уровне", не поддерживаются в теле функции. Все два оператора должны быть разделены точкой с запятой.

Примеры определяемых пользователем функций

В следующем разделе показаны примеры использования определяемых пользователем функций.

Определяемая пользователем функция, использующая инструкцию let

В следующем примере показана определяемая пользователем функция (лямбда),которая принимает идентификатор с именем параметра. Функция привязана к имени 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

Функции представления

Определяемая пользователем функция, которая не принимает аргументов и возвращает табличное выражение, можно пометить как представление. Маркировка определяемой пользователем функции как представления означает, что функция ведет себя как таблица при выполнении разрешения имен таблицы подстановочных знаков.

В следующем примере показаны две определяемые пользователем функции, T_view а T_notviewтакже показано, как разрешается только первая из них ссылкой на подстановочные знаки в следующей unionстатье:

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

Restrictions

Применяются следующие ограничения:

  • Определяемые пользователем функции не могут передавать сведения о вызове 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 это скалярная функция, которая ссылается на табличное выражение Table1 , но вызывается без ссылки на текущий контекст 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)

Неподдерживаемая скалярная функция

Следующий запрос не поддерживается, так как 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. Значения по умолчанию: значение по умолчанию для скалярного параметра функции должно быть скалярным литералом (константой).