Funciones de SQL estándar definidas por el usuario

BigQuery admite funciones definidas por el usuario (UDF). Una UDF te permite crear una función mediante otra expresión SQL o JavaScript. Estas funciones aceptan columnas de entrada y realizan acciones, para luego mostrar el resultado de esas acciones como un valor. Para obtener información sobre las funciones definidas por el usuario en SQL heredado, consulta Funciones definidas por el usuario en SQL heredado.

Las UDF pueden ser persistentes o temporales. Puedes reutilizar las UDF persistentes en varias consultas y las UDF temporales en una única consulta.

Sintaxis de las UDF

Para crear una UDF persistente, usa la siguiente sintaxis:

CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS]
    [[project_name.]dataset_name.]function_name
    ([named_parameter[, ...]])
  [RETURNS data_type]
  { sql_function_definition | javascript_function_definition }

Para crear una UDF temporal, usa la siguiente sintaxis:

CREATE [OR REPLACE] {TEMPORARY | TEMP} FUNCTION [IF NOT EXISTS]
    function_name
    ([named_parameter[, ...]])
  [RETURNS data_type]
  { sql_function_definition | javascript_function_definition }

named_parameter:
  param_name param_type

sql_function_definition:
  AS (sql_expression)

javascript_function_definition:
  [determinism_specifier]
  LANGUAGE js
  [OPTIONS (library = library_array)]
  AS javascript_code

determinism_specifier:
  { DETERMINISTIC | NOT DETERMINISTIC }

Esta sintaxis consta de los siguientes componentes:

  • CREATE { FUNCTION | OR REPLACE FUNCTION | FUNCTION IF NOT EXISTS }. Crea una función o la actualiza. Para reemplazar una función existente con el mismo nombre, usa la palabra clave OR REPLACE. Usa la cláusula IF NOT EXISTS si deseas tratar la consulta como completada y quieres que no se realice ninguna acción en el caso de que ya exista una función con el mismo nombre.

  • project_name es el nombre del proyecto en el que crearás la función. La configuración predeterminada es el proyecto que ejecuta esta consulta de DDL. Si el nombre del proyecto contiene caracteres especiales, como dos puntos, debe estar entre acentos graves ` (ejemplo: `google.com:my_project`).

  • dataset_name es el nombre del conjunto de datos en el que crearás la función. El valor predeterminado es defaultDataset en la solicitud.

  • named_parameter. Consiste en un par de param_name y param_type separados por comas. El valor de param_type es un tipo de datos de BigQuery. Para una UDF de SQL, el valor de param_type también puede ser ANY TYPE.

  • determinism_specifier. Solo se aplica a las funciones definidas por el usuario de JavaScript. Proporciona una sugerencia a BigQuery para decidir si el resultado de la consulta se puede almacenar en caché. Puede ser uno de los siguientes valores:

    • DETERMINISTIC: La función siempre muestra el mismo resultado cuando se pasan los mismos argumentos. El resultado de la consulta puede almacenarse en caché. Por ejemplo, si la función add_one(i) siempre muestra i + 1, la función es determinista.

    • NOT DETERMINISTIC: La función no siempre muestra el mismo resultado cuando se pasa los mismos argumentos y, por lo tanto, no se puede almacenar en caché. Por ejemplo, si add_random(i) muestra i + rand(), la función no es determinista y BigQuery no usará resultados almacenados en caché.

      Si todas las funciones invocadas son DETERMINISTAS, BigQuery intentará almacenar en caché el resultado, a menos que los resultados no se puedan almacenar en caché por otros motivos. Para obtener más información, consulta la sección sobre cómo usar resultados de consultas almacenados en caché.

  • [RETURNS data_type]. Especifica el tipo de datos que muestra la función.

    • Si la función está definida en SQL, la cláusula RETURNS es opcional. Si se omite la cláusula RETURNS, BigQuery deduce el tipo de resultado de la función a partir del cuerpo de la función SQL cuando una consulta llama a la función.
    • Si la función está definida en JavaScript, la cláusula RETURNS es obligatoria. Si deseas obtener más información sobre los valores permitidos para data_type, consulta Tipos de datos de UDF de JavaScript admitidos.
  • AS (sql_expression). Especifica la expresión SQL que define la función.

  • [OPTIONS (library = library_array)]. Para una UDF de JavaScript, especifica un arreglo de bibliotecas de JavaScript para incluir en la definición de función.

  • AS javascript_code. Especifica la definición de una función de JavaScript. javascript_code es un literal de string.

Para borrar una función definida por el usuario persistente, usa la siguiente sintaxis:

DROP FUNCTION [IF EXISTS] [[project_name.]dataset_name.]function_name

Las UDF temporales vencen apenas finaliza la consulta, por lo que las declaraciones DROP FUNCTION solo son compatibles con UDF temporales en secuencias de comandos y procedimientos.

UDF de SQL

Crea UDF de SQL con la siguiente sintaxis:

CREATE [OR REPLACE] [TEMPORARY | TEMP] FUNCTION [IF NOT EXISTS]
    [[`project_name`.]dataset_name.]function_name
    ([named_parameter[, ...]])
  [RETURNS data_type]
  AS (sql_expression)

named_parameter:
  param_name param_type

Parámetros de UDF de SQL con plantilla

Un parámetro con plantilla con param_type = ANY TYPE puede coincidir con más de un tipo de argumento cuando se llama a la función.

  • Si hay más de un parámetro con el tipo ANY TYPE, BigQuery no impone ninguna relación entre estos argumentos.
  • El tipo de datos que se muestra como resultado de la función no puede ser ANY TYPE. Debe omitirse, lo que significa que se determina de forma automática en función de sql_expression, o debe ser un tipo explícito.
  • Si pasas los argumentos de funciones de tipos que son incompatibles con la definición de la función, se producirá un error durante la llamada.

Ejemplos de UDF de SQL

En el siguiente ejemplo se muestra una UDF que emplea una función de SQL.

CREATE TEMP FUNCTION addFourAndDivide(x INT64, y INT64) AS ((x + 4) / y);
WITH numbers AS
  (SELECT 1 as val
  UNION ALL
  SELECT 3 as val
  UNION ALL
  SELECT 4 as val
  UNION ALL
  SELECT 5 as val)
SELECT val, addFourAndDivide(val, 2) AS result
FROM numbers;

+-----+--------+
| val | result |
+-----+--------+
| 1   | 2.5    |
| 3   | 3.5    |
| 4   | 4      |
| 5   | 4.5    |
+-----+--------+

En el siguiente ejemplo se muestra una UDF de SQL que usa un parámetro con plantilla. La función resultante acepta argumentos de varios tipos.

CREATE TEMP FUNCTION addFourAndDivideAny(x ANY TYPE, y ANY TYPE) AS (
  (x + 4) / y
);
SELECT addFourAndDivideAny(3, 4) AS integer_output,
       addFourAndDivideAny(1.59, 3.14) AS floating_point_output;

+----------------+-----------------------+
| integer_output | floating_point_output |
+----------------+-----------------------+
| 1.75           | 1.7802547770700636    |
+----------------+-----------------------+

En el siguiente ejemplo, se muestra una UDF de SQL que usa un parámetro con plantilla para mostrar el último elemento de un arreglo de cualquier tipo.

CREATE TEMP FUNCTION lastArrayElement(arr ANY TYPE) AS (
  arr[ORDINAL(ARRAY_LENGTH(arr))]
);
SELECT
  names[OFFSET(0)] AS first_name,
  lastArrayElement(names) AS last_name
FROM (
  SELECT ['Fred', 'McFeely', 'Rogers'] AS names UNION ALL
  SELECT ['Marie', 'Skłodowska', 'Curie']
);

+------------+-----------+
| first_name | last_name |
+------------+-----------+
| Fred       | Rogers    |
| Marie      | Curie     |
+------------+-----------+

UDF de JavaScript

Crea una UDF de JavaScript mediante la siguiente estructura.

CREATE [OR REPLACE] [TEMPORARY | TEMP] FUNCTION [IF NOT EXISTS]
    [[`project_name`.]dataset_name.]function_name
    ([named_parameter[, ...]])
  RETURNS data_type
  [DETERMINISTIC | NOT DETERMINISTIC]
  LANGUAGE js
  [OPTIONS (library = library_array)]
  AS javascript_code

Tipos de datos de UDF de JavaScript admitidos

Algunos tipos de SQL se corresponden de forma directa con tipos de JavaScript, pero otros no. BigQuery representa los tipos de la siguiente manera:

Tipo de datos de BigQuery Tipo de datos de JavaScript
ARRAY ARRAY
BOOL BOOLEAN
BYTES STRING codificada en base64
FLOAT64 NUMBER
NUMERIC Si un valor NUMERIC se puede representar de forma exacta como un valor de punto flotante IEEE 754 y no tiene una parte fraccionaria, se codifica como un Número. Estos valores están en el rango [-253, 253]. De lo contrario, se codifica como una String.
STRING STRING
STRUCT OBJECT en el que cada campo de STRUCT tiene nombre
TIMESTAMP DATE con un campo de microsegundos que contiene la fracción de microsecond de la marca de tiempo
DATE DATE

Debido a que JavaScript no admite un tipo de número entero de 64 bits, INT64 no es compatible como tipo de entrada para UDF de JavaScript. En su lugar, usa FLOAT64 a fin de representar valores como un número entero, o STRING para representarlos como una string.

BigQuery admite INT64 como un tipo de datos que se muestra en las UDF de JavaScript. En este caso, el cuerpo de la función de JavaScript puede mostrar una String o un Número de JavaScript. BigQuery luego convierte cualquiera de estos tipos en INT64.

Si el valor de retorno del UDF de JavaScript es una Promise, BigQuery espera hasta que se establezca la Promise. Si Promise se establece en un estado entregado, BigQuery muestra su resultado. Si Promise se establece en un estado rechazado, BigQuery muestra un error.

Reglas de citación

Debes encerrar el código de JavaScript entre comillas. Para fragmentos de código simples de una línea, puedes usar una string entre comillas estándar:

CREATE TEMP FUNCTION plusOne(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
AS "return x+1;";
SELECT val, plusOne(val) AS result
FROM UNNEST([1, 2, 3, 4, 5]) AS val;

+-----------+-----------+
| val       | result    |
+-----------+-----------+
| 1         | 2         |
| 2         | 3         |
| 3         | 4         |
| 4         | 5         |
| 5         | 6         |
+-----------+-----------+

En los casos en que el fragmento contiene comillas o consta de varias líneas, usa bloques con comillas triples:

CREATE TEMP FUNCTION customGreeting(a STRING)
RETURNS STRING
LANGUAGE js AS """
  var d = new Date();
  if (d.getHours() < 12) {
    return 'Good Morning, ' + a + '!';
  } else {
    return 'Good Evening, ' + a + '!';
  }
  """;
SELECT customGreeting(names) as everyone
FROM UNNEST(["Hannah", "Max", "Jakob"]) AS names;
+-----------------------+
| everyone              |
+-----------------------+
| Good Morning, Hannah! |
| Good Morning, Max!    |
| Good Morning, Jakob!  |
+-----------------------+

Incluye bibliotecas de JavaScript

Puedes extender tus UDF de JavaScript con la sección OPTIONS. En esta sección, se permite especificar bibliotecas de código externas para la UDF.

CREATE TEMP FUNCTION myFunc(a FLOAT64, b STRING)
  RETURNS STRING
  LANGUAGE js
  OPTIONS (
    library=["gs://my-bucket/path/to/lib1.js", "gs://my-bucket/path/to/lib2.js"]
  )
  AS
"""
    // Assumes 'doInterestingStuff' is defined in one of the library files.
    return doInterestingStuff(a, b);
""";

SELECT myFunc(3.14, 'foo');

En el ejemplo anterior, el código en lib1.js y lib2.js está disponible para cualquier código en la sección [external_code] de la UDF.

Ejemplos de UDF de JavaScript

CREATE TEMP FUNCTION multiplyInputs(x FLOAT64, y FLOAT64)
RETURNS FLOAT64
LANGUAGE js AS """
  return x*y;
""";
WITH numbers AS
  (SELECT 1 AS x, 5 as y
  UNION ALL
  SELECT 2 AS x, 10 as y
  UNION ALL
  SELECT 3 as x, 15 as y)
SELECT x, y, multiplyInputs(x, y) as product
FROM numbers;

+-----+-----+--------------+
| x   | y   | product      |
+-----+-----+--------------+
| 1   | 5   | 5            |
| 2   | 10  | 20           |
| 3   | 15  | 45           |
+-----+-----+--------------+

Puedes pasar el resultado de una UDF como entrada para otra UDF. Por ejemplo:

CREATE TEMP FUNCTION multiplyInputs(x FLOAT64, y FLOAT64)
RETURNS FLOAT64
LANGUAGE js AS """
  return x*y;
""";
CREATE TEMP FUNCTION divideByTwo(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js AS """
  return x/2;
""";
WITH numbers AS
  (SELECT 1 AS x, 5 as y
  UNION ALL
  SELECT 2 AS x, 10 as y
  UNION ALL
  SELECT 3 as x, 15 as y)
SELECT x,
  y,
  multiplyInputs(divideByTwo(x), divideByTwo(y)) as half_product
FROM numbers;

+-----+-----+--------------+
| x   | y   | half_product |
+-----+-----+--------------+
| 1   | 5   | 1.25         |
| 2   | 10  | 5            |
| 3   | 15  | 11.25        |
+-----+-----+--------------+

En el siguiente ejemplo, se suman los valores de todos los campos llamados foo en la string JSON dada.

CREATE TEMP FUNCTION SumFieldsNamedFoo(json_row STRING)
  RETURNS FLOAT64
  LANGUAGE js AS """
function SumFoo(obj) {
  var sum = 0;
  for (var field in obj) {
    if (obj.hasOwnProperty(field) && obj[field] != null) {
      if (typeof obj[field] == "object") {
        sum += SumFoo(obj[field]);
      } else if (field == "foo") {
        sum += obj[field];
      }
    }
  }
  return sum;
}
var row = JSON.parse(json_row);
return SumFoo(row);
""";

WITH Input AS (
  SELECT STRUCT(1 AS foo, 2 AS bar, STRUCT('foo' AS x, 3.14 AS foo) AS baz) AS s, 10 AS foo UNION ALL
  SELECT NULL, 4 AS foo UNION ALL
  SELECT STRUCT(NULL, 2 AS bar, STRUCT('fizz' AS x, 1.59 AS foo) AS baz) AS s, NULL AS foo
)
SELECT
  TO_JSON_STRING(t) AS json_row,
  SumFieldsNamedFoo(TO_JSON_STRING(t)) AS foo_sum
FROM Input AS t;
+---------------------------------------------------------------------+---------+
| json_row                                                            | foo_sum |
+---------------------------------------------------------------------+---------+
| {"s":{"foo":1,"bar":2,"baz":{"x":"foo","foo":3.14}},"foo":10}       | 14.14   |
| {"s":null,"foo":4}                                                  | 4       |
| {"s":{"foo":null,"bar":2,"baz":{"x":"fizz","foo":1.59}},"foo":null} | 1.59    |
+---------------------------------------------------------------------+---------+

Para obtener más información sobre cómo se asignan los tipos de datos de BigQuery a los tipos de JavaScript, consulta Tipos de datos de UDF de JavaScript admitidos.

Recomendaciones para las UDF de JavaScript

Realiza un filtro previo a tu entrada

Si tu entrada se puede filtrar con facilidad antes de pasarla a una UDF de JavaScript, es probable que tu consulta sea más rápida y económica.

Evita el estado mutable persistente

No almacenes ni accedas a un estado mutable en llamadas a la UDF de JavaScript.

Usa la memoria de manera eficaz

El entorno de procesamiento de JavaScript tiene un límite de memoria disponible por consulta. Las consultas de UDF de JavaScript que acumulan demasiado estado local pueden fallar debido al agotamiento de la memoria.

Ejecuta una consulta con una UDF

Las UDF y la IU web de BigQuery

Puedes usar la IU web de BigQuery para ejecutar consultas con una o más UDF.

  1. Haz clic en el botón REDACTAR CONSULTA.
  2. Haz clic en la pestaña Editor de consultas.
  3. Haz clic en el botón Mostrar opciones.
  4. Desmarca la casilla de verificación Usa SQL heredado.
  5. Escribe la declaración de UDF en el área de texto Editor de consultas. Por ejemplo:

    CREATE TEMPORARY FUNCTION timesTwo(x FLOAT64)
    RETURNS FLOAT64
      LANGUAGE js AS """
      return x*2;
    """;
  6. Escribe tu consulta debajo de la declaración de UDF. Por ejemplo:

    SELECT timesTwo(numbers) AS doubles
    FROM UNNEST([1, 2, 3, 4, 5]) AS numbers;
  7. Haz clic en el botón EJECUTAR CONSULTA. Los resultados de la consulta se muestran debajo de los botones.

Las UDF y la herramienta de línea de comandos de bq

Puedes usar la herramienta de línea de comandos de bq del SDK de Cloud para ejecutar una consulta que contenga una o más UDF.

Usa la siguiente sintaxis para ejecutar una consulta con una UDF:

bq query <statement_with_udf_and_query>

UDF autorizadas

Una UDF autorizada es una UDF que está autorizada para acceder a un conjunto de datos en particular. La UDF puede consultar tablas en el conjunto de datos, incluso si el usuario que llama a la UDF no tiene acceso a esas tablas.

Las UDF autorizadas te permiten compartir resultados de consultas con usuarios o grupos específicos sin darles a esos usuarios o grupos acceso a las tablas subyacentes. Por ejemplo, una UDF autorizada puede calcular una agregación en los datos o buscar un valor de tabla y usar ese valor en un cálculo.

Para autorizar una UDF, puedes usar la API de REST o la herramienta de línea de comandos de bq:

API

  1. Llama al método datasets.get para recuperar el conjunto de datos al que deseas que acceda la UDF. El cuerpo de la respuesta contiene una representación del recurso Dataset.

  2. Agrega el siguiente objeto JSON al arreglo access en el recurso Dataset:

    {
     "routine": {
       "datasetId": "DATASET_NAME",
       "projectId": "PROJECT_ID",
       "routineId": "ROUTINE_NAME"
     }
    }
    

    Aquí:

    • DATASET_NAME es el nombre del conjunto de datos que contiene la UDF.
    • PROJECT_ID es el ID del proyecto que contiene la UDF.
    • ROUTINE_NAME es el nombre de la UDF.
  3. Llama al método dataset.update con la representación Dataset modificada.

bq

  1. Usa el comando bq show para obtener la representación JSON del conjunto de datos al que deseas que acceda la UDF. El resultado del comando es una representación JSON del recurso Dataset. Guarda el resultado en un archivo local.

    bq show --format=prettyjson TARGET_DATASET > dataset.json
    

    Reemplaza TARGET_DATASET por el nombre del conjunto de datos al que tendrá acceso la UDF.

  2. Edita el archivo para agregar el siguiente objeto JSON al arreglo access en el recurso Dataset:

    {
     "routine": {
       "datasetId": "DATASET_NAME",
       "projectId": "PROJECT_ID",
       "routineId": "ROUTINE_NAME"
     }
    }
    

    Aquí:

    • DATASET_NAME es el nombre del conjunto de datos que contiene la UDF.
    • PROJECT_ID es el ID del proyecto que contiene la UDF.
    • ROUTINE_NAME es el nombre de la UDF.
  3. Usa el comando bq update para actualizar el conjunto de datos.

    bq update --source dataset.json TARGET_DATASET
    

Ejemplo de una UDF autorizada

A continuación, se muestra un ejemplo de creación y uso de una UDF autorizada.

  1. Crea dos conjuntos de datos llamados private_dataset y public_dataset. Para obtener más información sobre la creación de un conjunto de datos, consulta Crea un conjunto de datos.

  2. Ejecuta la siguiente declaración para crear una tabla llamada private_table en private_dataset:

    CREATE OR REPLACE TABLE private_dataset.private_table
    AS SELECT key FROM UNNEST(['key1', 'key1','key2','key3']) key;
    
  3. Ejecuta la siguiente declaración para crear una UDF llamada count_key en public_dataset. La UDF incluye una declaración SELECT en private_table.

    CREATE OR REPLACE FUNCTION public_dataset.count_key(input_key STRING)
    RETURNS INT64
    AS
    ((SELECT COUNT(1) FROM private_dataset.private_table t WHERE t.key = input_key));
    
  4. Otorga la función bigquery.dataViewer a un usuario en el conjunto de datos public_dataset. Esta función incluye el permiso bigquery.routines.get, que le permite al usuario llamar a la función. Para obtener información sobre cómo asignar controles de acceso a un conjunto de datos, consulta Controla el acceso a los conjuntos de datos.

  5. En este punto, el usuario tiene permiso para llamar a la función count_key, pero no puede acceder a la tabla en private_dataset. Si el usuario intenta llamar a la función, recibirá un mensaje de error similar al siguiente:

    Access Denied: Table myproject:private_dataset.private_table: User does
    not have permission to query table myproject:private_dataset.private_table.
    
  6. Con la herramienta de línea de comandos de bq, ejecuta el comando show de la siguiente manera:

    bq show --format=prettyjson private_dataset > dataset.json
    

    El resultado se guarda en un archivo local llamado dataset.json.

  7. Edita dataset.json para agregar el siguiente objeto JSON al arreglo access:

    {
     "routine": {
       "datasetId": "public_dataset",
       "projectId": "PROJECT_ID",
       "routineId": "count_key"
     }
    }
    

    Reemplaza PROJECT_ID por el ID del proyecto para public_dataset.

  8. Con la herramienta de línea de comandos de bq, ejecuta el comando update de la siguiente manera:

    bq update --source dataset.json private_dataset
    
  9. Para verificar que la UDF tenga acceso a private_dataset, el usuario puede ejecutar la siguiente consulta:

    SELECT public_dataset.count_key('key1');
    

Agrega descripciones a las UDF

Para agregar una descripción a una UDF, sigue estos pasos:

Console

  1. Abre la IU web de BigQuery en Cloud Console.

    Ir a Cloud Console

  2. Selecciona tu función en el panel Recursos.

  3. En el panel Detalles, haz clic en el ícono de lápiz junto a Descripción para editar el texto de la descripción.

  4. En el diálogo, ingresa una descripción en la casilla o edita la descripción existente. Haz clic en Actualizar para guardar el nuevo texto de la descripción.

Como alternativa, puedes usar una consulta de SQL estándar a fin de actualizar la descripción mediante el parámetro description del campo OPTIONS. En el cuadro Editor de consultas, ingresa la definición de la función y, luego, agrega la siguiente línea:

OPTIONS (description="DESCRIPTION") AS """

Reemplaza DESCRIPTION por la descripción que deseas agregar.

bq

Puedes editar la descripción de una función desde la línea de comandos mediante la sintaxis bq query de las UDF y la herramienta de línea de comandos de bq. Especifica SQL estándar con una marca --nouse_legacy_sql o -- use_legacy_sql=false y, luego, ingresa la definición de tu función. Para establecer el parámetro description en el campo OPTIONS, agrega la siguiente línea a tu definición:

OPTIONS (description="DESCRIPTION") AS """

Reemplaza DESCRIPTION por la descripción que deseas agregar.

Limitaciones

Las siguientes limitaciones se aplican a funciones temporales y persistentes definidas por el usuario:

  • No se admiten los objetos DOM Window, Document y Node, ni las funciones que los requieren.
  • No se admiten las funciones de JavaScript que se basan en el código nativo.
  • Una UDF de JavaScript puede agotar el tiempo de espera y evitar que se complete tu consulta. Los tiempos de espera pueden ser de tan solo 5 minutos, pero pueden variar según distintos factores, incluidos el tiempo de CPU del usuario que consume tu función y el tamaño de las entradas y salidas en la función de JavaScript.
  • Las operaciones a nivel de bits en JavaScript manejan solo los 32 bits más significativos
  • Las UDF están sujetas a ciertos límites de frecuencia y límites de cuota. Para obtener más información, consulta Límites de UDF.

Las siguientes limitaciones se aplican a las funciones persistentes definidas por el usuario:

  • Cada conjunto de datos solo puede contener una UDF persistente con el mismo nombre. Sin embargo, puedes crear una UDF cuyo nombre sea igual al de una tabla en el mismo conjunto de datos.
  • Cuando hagas referencia a una UDF persistente desde otra UDF persistente o una vista lógica, debes calificar el nombre con el conjunto de datos. Por ejemplo:
    CREATE FUNCTION mydataset.referringFunction() AS (mydataset.referencedFunction());

Las limitaciones siguientes se aplican a las funciones temporales definidas por el usuario.

  • Cuando creas una UDF temporal, el function_name no puede contener puntos.
  • Las vistas y las UDF persistentes no pueden hacer referencia a las UDF temporales.