Share via


Funciones definidas por el usuario

Las funciones definidas por el usuario son subconsultas reutilizables que se pueden definir como parte de la propia consulta (funciones definidas por consultas) o almacenadas como parte de los metadatos de la base de datos (funciones almacenadas). Las funciones definidas por el usuario se invocan a través de un nombre, reciben cero o más argumentos de entrada (que pueden ser escalares o tabulares) y generan un valor único (que puede ser escalar o tabular) en función del cuerpo de la función.

Una función definida por el usuario pertenece a una de las siguientes dos categorías:

  • Funciones escalares
  • Funciones tabulares

Los argumentos de entrada y la salida de la función determinan si se trata de una función escalar o tabular, que indica cómo se podría usar.

Para optimizar varios usos de las funciones definidas por el usuario dentro de una sola consulta, consulte Optimización de consultas que usan expresiones con nombre.

Función escalar

  • Tiene cero argumentos de entrada, o todos los argumentos de entrada son valores escalares.
  • Genera un único valor escalar.
  • Se puede usar siempre que se permita una expresión escalar.
  • Solo puede usar el contexto de fila en el que se define.
  • Solo puede hacer referencia a tablas (y vistas) que están en el esquema accesible.

Función tabular

  • Acepta uno o varios argumentos de entrada tabulares, y cero o más argumentos de entrada escalares, o:
  • Genera un único valor tabular.

Nombres de función

Los nombres de función definidos por el usuario válidos deben seguir las mismas reglas de nomenclatura de identificador que otras entidades.

El nombre también debe ser único en el ámbito de su definición.

Nota

Si una función almacenada y una tabla tienen el mismo nombre, cualquier referencia a ese nombre se resuelve en la función almacenada, no en el nombre de la tabla. En su lugar, use la función table para hacer referencia a la tabla.

Argumentos de entrada

Las funciones definidas por el usuario válidas siguen las reglas siguientes:

  • Una función definida por el usuario tiene una lista fuertemente tipada de cero o más argumentos de entrada.
  • Un argumento de entrada tiene un nombre, un tipo y, en el caso de los argumentos escalares, un valor predeterminado.
  • El nombre de un argumento de entrada es un identificador.
  • El tipo de un argumento de entrada puede ser cualquiera de los tipos de datos escalares o un esquema tabular.

En términos de sintaxis, la lista de argumentos de entrada es una lista separada por comas de definiciones de argumentos, que se colocan entre paréntesis. Cada definición de argumento se especifica de la siguiente manera

ArgName:ArgType [= ArgDefaultValue]

En el caso de los argumentos tabulares, ArgType tiene la misma sintaxis que la definición de tabla (paréntesis y una lista de pares de nombre y tipo de columna), con la adición de un aislamiento (*) que indica "cualquier esquema tabular".

Por ejemplo:

Sintaxis Descripción de la lista de argumentos de entrada
() Sin argumentos
(s:string) Argumento escalar único denominado s que toma un valor de tipo string.
(a:long, b:bool=true) Dos argumentos escalares, el segundo de los cuales tiene un valor predeterminado.
(T1:(*), T2(r:real), b:bool) Tres argumentos (dos argumentos tabulares y un argumento escalar).

Nota

Cuando se usan argumentos de entrada tabulares y escalares, se todos los argumentos de entrada tabulares se colocan delante de los argumentos de entrada escalares.

Ejemplos

Función escalar

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

Función tabular sin argumentos

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

Función tabular con argumentos

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

Salida

x
9
10

Una función tabular que usa una entrada tabular sin ninguna columna especificada. Cualquier tabla se puede pasar a una función y no se puede hacer referencia a ninguna columna de la tabla dentro de la función.

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

Salida

x
1
2
3

Declaración de funciones definidas por el usuario

La declaración de una función definida por el usuario especifica lo siguiente:

  • Nombre de la función
  • Esquema de la función (los parámetros que acepta , si los hay)
  • Cuerpo de la función

Nota

No se admite la sobrecarga de funciones. No se pueden crear varias funciones con el mismo nombre y distintos esquemas de entrada.

Sugerencia

Las funciones lambda no tienen un nombre y están enlazadas a un nombre mediante una instrucción Let. Por lo tanto, se pueden considerar como funciones almacenadas definidas por el usuario. Ejemplo: Declaración de una función lambda que acepta dos argumentos (un valor string denominado s y un valor long denominado i). Devuelve el producto del primer valor (después de convertirlo en un número) y el segundo. La expresión lambda está enlazada al nombre f:

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

El cuerpo de la función incluye:

  • Exactamente una expresión, que proporciona el valor devuelto de la función (un valor escalar o tabular).
  • Cualquier número (cero o más) de instrucciones Let, cuyo ámbito es el cuerpo de la función. Si se especifican, las instrucciones Let deben preceder a la expresión que define el valor devuelto de la función.
  • Cualquier número (cero o más) de instrucciones de parámetros de consulta, que declaran los parámetros de consulta que usa la función. Si se especifican, deben preceder a la expresión que define el valor devuelto de la función.

Nota

Otros tipos de instrucciones de consulta que se admiten en el "nivel superior" de la consulta no se admiten dentro del cuerpo de una función. Las instrucciones deben estar separadas por un punto y coma.

Ejemplos de funciones definidas por el usuario

En la sección siguiente se muestran ejemplos de cómo usar funciones definidas por el usuario.

Función definida por el usuario que usa una instrucción Let

En el ejemplo siguiente se muestra una función definida por el usuario (lambda) que acepta un parámetro denominado ID. La función está enlazada al nombre Test y usa tres instrucciones let , en las que la definición test3 usa el parámetro ID . Cuando se ejecuta, la salida de la consulta es 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

Función definida por el usuario que define un valor predeterminado para un parámetro

En el ejemplo siguiente se muestra una función que acepta tres argumentos. Los dos últimos tienen un valor predeterminado y no tienen que estar presentes en el sitio de llamada.

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"

Invocación de una función definida por el usuario

El método para invocar una función definida por el usuario depende de los argumentos que la función espera recibir. En las secciones siguientes se explica cómo invocar una UDF sin argumentos, invocar una UDF con argumentos escalares e invocar una UDF con argumentos tabulares.

Invocación de una función definida por el usuario sin argumentos

Una función definida por el usuario que no toma ningún argumento y se puede invocar por su nombre, o por su nombre y una lista de argumentos vacía entre paréntesis.

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

Invocación de una UDF con argumentos escalares

Se puede invocar una función definida por el usuario que toma uno o varios argumentos escalares mediante el nombre de la función y una lista de argumentos concreta entre paréntesis:

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

Invocación de una UDF con argumentos tabulares

Función definida por el usuario que toma uno o varios argumentos de tabla (con cualquier número de argumentos escalares) y se puede invocar mediante el nombre de la función y una lista de argumentos concreta entre paréntesis:

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

También puede usar el operador invoke para invocar una función definida por el usuario que acepta uno o más argumentos de tabla y devuelve una tabla. Esta función resulta útil cuando el primer argumento de tabla concreto para la función es el origen del operador 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(":-)")

Valores predeterminados

Las funciones pueden especificar valores predeterminados para algunos de sus parámetros en las siguientes condiciones:

  • Solo se pueden proporcionar valores predeterminados para los parámetros escalares.
  • Los valores predeterminados siempre son literales (constantes). No pueden ser cálculos arbitrarios.
  • Los parámetros sin valores predeterminados siempre preceden a los parámetros que tienen un valor predeterminado.
  • Los autores de llamadas deben proporcionar el valor de todos los parámetros sin valores predeterminados organizados en el mismo orden que la declaración de función.
  • Los autores de las llamadas no necesitan especificar el valor de los parámetros con valores predeterminados, pero pueden hacerlo.
  • Los autores de las llamadas pueden especificar los argumentos en un orden que no coincida con el orden de los parámetros. Si es este el caso, deben asignar un nombre a los argumentos.

En el ejemplo siguiente se devuelve una tabla con dos registros idénticos. En la primera invocación de f, los argumentos están completamente "revueltos", por lo que cada uno de ellos recibe un nombre de manera explícita:

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"

Salida

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

Funciones de vista

Una función definida por el usuario que no acepta ningún argumento y devuelve una expresión tabular se puede marcar como una vista. Marcar una función definida por el usuario como una vista significa que la función se comporta como una tabla cada vez que se realiza una resolución de nombres de tabla comodín.

En el ejemplo siguiente se muestran dos funciones definidas por el usuario, T_view y T_notview, y se muestra cómo solo la primera se resuelve mediante la referencia de un carácter comodín en el operador union:

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

Restricciones

Se aplican las restricciones que se indican a continuación:

  • Las funciones definidas por el usuario no se pueden pasar a información de invocación de toscalar() que depende del contexto de la fila en la que se llama a la función.
  • Las funciones definidas por el usuario que devuelven una expresión tabular no se pueden invocar con un argumento que varíe según el contexto de la fila.
  • No se puede invocar una función que contenga al menos una entrada tabular en un clúster remoto.
  • No se puede invocar una función escalar en un clúster remoto.

El único lugar en el que se puede invocar una función definida por el usuario con un argumento que varíe según el contexto de la fila es cuando la función definida por el usuario se compone solo de funciones escalares y no usa toscalar().

Ejemplos

Función escalar admitida

Se admite la consulta siguiente porque f es una función escalar que no hace referencia a ninguna expresión tabular.

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)

Se admite la consulta siguiente porque f es una función escalar que hace referencia a la expresión Table1 tabular, pero no se invoca con ninguna referencia al contexto f(10)de fila actual:

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)

Función escalar no admitida

No se admite la consulta siguiente porque f es una función escalar que hace referencia a la expresión Table1tabular y se invoca con una referencia al contexto f(Column)de fila actual:

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)

Función tabular no admitida

No se admite la consulta siguiente porque f es una función tabular que se invoca en un contexto que espera un valor escalar.

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)

Características que actualmente no admiten las funciones definidas por el usuario

Por motivos de integridad, estas son algunas características solicitadas habitualmente para las funciones definidas por el usuario que actualmente no se admiten:

  1. Sobrecarga de funciones: actualmente no hay ninguna manera de sobrecargar una función (una manera de crear varias funciones con el mismo nombre y un esquema de entrada diferente).

  2. Valores predeterminados: el valor predeterminado de un parámetro escalar para una función debe ser un literal escalar (una constante).