Funzioni aggregate definite dall'utente

Le funzioni aggregate definite dall'utente (UDAF) in BigQuery consentono di creare una logica di aggregazione personalizzata utilizzando il codice SQL o JavaScript. Puoi elaborare le colonne di input come gruppo per eseguire calcoli specializzati su più righe alla volta e restituire il risultato come un singolo valore.

Crea una funzione definita dall'utente di aggregazione SQL

Questa sezione descrive i vari modi in cui puoi creare una funzione SQL definita dall'utente (UDAF) permanente o temporanea in BigQuery.

Crea una funzione UDAF SQL permanente

Puoi creare una funzione definita dall'utente di aggregazione SQL permanente, il che significa che puoi riutilizzarla in più query. Le UDAF persistenti possono essere chiamate in sicurezza quando vengono condivise tra i proprietari. Le UDAF non possono modificare i dati, comunicare con sistemi esterni o inviare log a Google Cloud Observability o ad applicazioni simili.

Per creare una UDAF permanente, utilizza l'istruzione CREATE AGGREGATE FUNCTION senza la parola chiave TEMP o TEMPORARY. Devi includere il set di dati nel percorso della funzione.

Ad esempio, la seguente query crea una funzione UDAF permanente denominata ScaledAverage:

CREATE AGGREGATE FUNCTION myproject.mydataset.ScaledAverage(
  dividend FLOAT64,
  divisor FLOAT64)
RETURNS FLOAT64
AS (
  AVG(dividend / divisor)
);

Crea una funzione UDAF SQL temporanea

Puoi creare una UDAF SQL temporanea, ovvero che esiste solo nell'ambito di una singola query, script, sessione o procedura.

Per creare una UDAF temporanea, utilizza l'istruzione CREATE AGGREGATE FUNCTION con la parola chiave TEMP o TEMPORARY.

Ad esempio, la seguente query crea una funzione UDAF temporanea denominata ScaledAverage:

CREATE TEMP AGGREGATE FUNCTION ScaledAverage(
  dividend FLOAT64,
  divisor FLOAT64)
RETURNS FLOAT64
AS (
  AVG(dividend / divisor)
);

Utilizza parametri aggregati e non aggregati

Puoi creare una funzione UDAF SQL con parametri aggregati e non aggregati.

In genere, le funzioni aggregate definite dall'utente aggregano i parametri della funzione in tutte le righe di un gruppo. Tuttavia, puoi specificare un parametro della funzione come non aggregato con la parola chiave NOT AGGREGATE.

Un parametro di funzione non aggregata è un parametro di funzione scalare con un valore costante per tutte le righe di un gruppo. Un parametro di funzione non aggregata valido deve essere un valore letterale. All'interno della definizione della funzione definita dall'utente (UDAF), i parametri della funzione di aggregazione possono apparire solo come argomenti della funzione per le chiamate di funzione di aggregazione. I riferimenti ai parametri delle funzioni non aggregate possono essere visualizzati in qualsiasi punto della definizione della UDAF.

Ad esempio, la seguente funzione contiene un parametro aggregato chiamato dividend e un parametro non aggregato chiamato divisor:

-- Create the function.
CREATE TEMP AGGREGATE FUNCTION ScaledSum(
  dividend FLOAT64,
  divisor FLOAT64 NOT AGGREGATE)
RETURNS FLOAT64
AS (
  SUM(dividend) / divisor
);

Utilizza il progetto predefinito nel corpo della funzione

Nel corpo di una UDAF SQL, tutti i riferimenti alle entità BigQuery, come tabelle o viste, devono includere l'ID progetto, a meno che l'entità non risieda nello stesso progetto che contiene la UDAF.

Ad esempio, prendi in esame la seguente affermazione:

CREATE AGGREGATE FUNCTION project1.dataset_a.ScaledAverage(
  dividend FLOAT64,
  divisor FLOAT64)
RETURNS FLOAT64
AS (
  ( SELECT AVG(dividend / divisor) FROM dataset_a.my_table )
);

Se esegui l'istruzione precedente nel progetto project1, l'istruzione ha esito positivo perché my_table esiste in project1. Tuttavia, se esegui l'istruzione precedente da un altro progetto, l'istruzione non va a buon fine. Per correggere l'errore, includi l'ID progetto nel riferimento alla tabella:

CREATE AGGREGATE FUNCTION project1.dataset_a.ScaledAverage(
  dividend FLOAT64,
  divisor FLOAT64)
RETURNS FLOAT64
AS (
  ( SELECT AVG(dividend / divisor) FROM project1.dataset_a.my_table )
);

Puoi anche fare riferimento a un'entità in un progetto o set di dati diverso da quello in cui crei la funzione:

CREATE AGGREGATE FUNCTION project1.dataset_a.ScaledAverage(
  dividend FLOAT64,
  divisor FLOAT64)
RETURNS FLOAT64
AS (
  ( SELECT AVG(dividend / divisor) FROM project2.dataset_c.my_table )
);

Crea una funzione JavaScript definita dall'utente di aggregazione

Questa sezione descrive i vari modi in cui puoi creare una UDAF JavaScript in BigQuery. Esistono alcune regole da osservare quando si crea una funzione UDAF JavaScript:

  • Il corpo della funzione UDAF JavaScript deve essere un valore letterale stringa tra virgolette che rappresenta il codice JavaScript. Per scoprire di più sui diversi tipi di valori letterali stringa tra virgolette che puoi utilizzare, consulta Formati per i valori letterali tra virgolette.

  • Sono consentite solo determinate codifiche dei tipi. Per ulteriori informazioni, consulta Codifiche dei tipi SQL consentite in una funzione UDAF JavaScript.

  • Il corpo della funzione JavaScript deve includere quattro funzioni JavaScript che inizializzano, aggregano, uniscono e finalizzano i risultati per la funzione definita dall'utente JavaScript (initialState, aggregate, merge e finalize). Per saperne di più, consulta Funzioni di aggregazione JavaScript obbligatorie.

  • Qualsiasi valore restituito dalla funzione initialState o lasciato nell'argomento state dopo la chiamata della funzione aggregate o merge deve essere serializzabile. Se vuoi utilizzare dati di aggregazione non serializzabili, come funzioni o campi di simboli, devi utilizzare le funzioni serialize e deserialize incluse. Per saperne di più, consulta Serializza e deserializza i dati in una funzione UDAF JavaScript.

Crea una funzione UDAF JavaScript permanente

Puoi creare una funzione definita dall'utente (UDAF) JavaScript permanente, il che significa che puoi riutilizzarla in più query. Le UDAF persistenti possono essere chiamate in sicurezza quando vengono condivise tra i proprietari. Le UDAF non possono modificare i dati, comunicare con sistemi esterni o inviare log a Google Cloud Observability o ad applicazioni simili.

Per creare una UDAF permanente, utilizza l'istruzione CREATE AGGREGATE FUNCTION senza la parola chiave TEMP o TEMPORARY. Devi includere il set di dati nel percorso della funzione.

La seguente query crea una funzione di aggregazione definita dall'utente (UDAF) JavaScript permanente denominata SumPositive:

CREATE OR REPLACE AGGREGATE FUNCTION my_project.my_dataset.SumPositive(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
AS r'''

  export function initialState() {
    return {sum: 0}
  }
  export function aggregate(state, x) {
    if (x > 0) {
      state.sum += x;
    }
  }
  export function merge(state, partialState) {
    state.sum += partialState.sum;
  }
  export function finalize(state) {
    return state.sum;
  }

''';

-- Call the JavaScript UDAF.
WITH numbers AS (
  SELECT * FROM UNNEST([1.0, -1.0, 3.0, -3.0, 5.0, -5.0]) AS x)
SELECT my_project.my_dataset.SumPositive(x) AS sum FROM numbers;

/*-----*
 | sum |
 +-----+
 | 9.0 |
 *-----*/

Crea una funzione UDAF JavaScript temporanea

Puoi creare una UDAF JavaScript temporanea, ovvero che esiste solo nell'ambito di una singola query, script, sessione o procedura.

Per creare una UDAF temporanea, utilizza l'istruzione CREATE AGGREGATE FUNCTION con la parola chiave TEMP o TEMPORARY.

La seguente query crea una funzione UDAF JavaScript temporanea denominata SumPositive:

CREATE TEMP AGGREGATE FUNCTION SumPositive(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
AS r'''

  export function initialState() {
    return {sum: 0}
  }
  export function aggregate(state, x) {
    if (x > 0) {
      state.sum += x;
    }
  }
  export function merge(state, partialState) {
    state.sum += partialState.sum;
  }
  export function finalize(state) {
    return state.sum;
  }

''';

-- Call the JavaScript UDAF.
WITH numbers AS (
  SELECT * FROM UNNEST([1.0, -1.0, 3.0, -3.0, 5.0, -5.0]) AS x)
SELECT SumPositive(x) AS sum FROM numbers;

/*-----*
 | sum |
 +-----+
 | 9.0 |
 *-----*/

Includi parametri non aggregati in una funzione JavaScript definita dall'utente (UDAF)

Puoi creare una funzione UDAF JavaScript con parametri aggregati e non aggregati.

In genere, le UDAF aggregano i parametri della funzione in tutte le righe di un gruppo. Tuttavia, puoi specificare un parametro della funzione come non aggregato con la parola chiave NOT AGGREGATE.

Un parametro di funzione non aggregata è un parametro di funzione scalare con un valore costante per tutte le righe di un gruppo. Un parametro di funzione non aggregata valido deve essere un valore letterale. All'interno della definizione della funzione definita dall'utente (UDAF), i parametri della funzione di aggregazione possono apparire solo come argomenti della funzione per le chiamate di funzione di aggregazione. I riferimenti ai parametri delle funzioni non aggregate possono essere visualizzati in qualsiasi punto della definizione della UDAF.

Nel seguente esempio, la funzione UDAF JavaScript contiene un parametro aggregato chiamato s e un parametro non aggregato chiamato delimiter:

CREATE TEMP AGGREGATE FUNCTION JsStringAgg(
  s STRING,
  delimiter STRING NOT AGGREGATE)
RETURNS STRING
LANGUAGE js
AS r'''

  export function initialState() {
    return {strings: []}
  }
  export function aggregate(state, s) {
    state.strings.push(s);
  }
  export function merge(state, partialState) {
    state.strings = state.strings.concat(partialState.strings);
  }
  export function finalize(state, delimiter) {
    return state.strings.join(delimiter);
  }

''';

-- Call the JavaScript UDAF.
WITH strings AS (
  SELECT * FROM UNNEST(["aaa", "bbb", "ccc", "ddd"]) AS values)
SELECT JsStringAgg(values, '.') AS result FROM strings;

/*-----------------*
 | result          |
 +-----------------+
 | aaa.bbb.ccc.ddd |
 *-----------------*/

Serializza e deserializza i dati in una funzione di aggregazione definita dall'utente (UDAF) JavaScript

BigQuery deve serializzare qualsiasi oggetto restituito dalla funzione initialState o che rimane nell'argomento state dopo la chiamata della funzione aggregate o merge. BigQuery supporta la serializzazione di un oggetto se tutti i campi sono uno dei seguenti:

  • Un valore primitivo JavaScript (ad esempio: 2, "abc", null, undefined).
  • Un oggetto JavaScript per il quale BigQuery supporta la serializzazione di tutti i valori dei campi.
  • Un array JavaScript per il quale BigQuery supporta la serializzazione di tutti gli elementi.

I seguenti valori restituiti sono serializzabili:

export function initialState() {
  return {a: "", b: 3, c: null, d: {x: 23} }
}
export function initialState() {
  return {value: 2.3};
}

I seguenti valori restituiti non sono serializzabili:

export function initialState() {
  return {
    value: function() {return 6;}
  }
}
export function initialState() {
  return 2.3;
}

Se vuoi lavorare con stati di aggregazione non serializzabili, la funzione UDAF JavaScript deve includere le funzioni serialize e deserialize. La funzione serialize converte lo stato di aggregazione in un oggetto serializzabile; la funzione deserialize riconverte l'oggetto serializzabile in uno stato di aggregazione.

Nell'esempio seguente, una libreria esterna calcola le somme utilizzando un'interfaccia:

export class SumAggregator {
 constructor() {
   this.sum = 0;
 }
 update(value) {
   this.sum += value;
 }
 getSum() {
   return this.sum;
 }
}

La seguente query non viene eseguita perché l'oggetto della classe SumAggregator non è serializzabile in BigQuery a causa della presenza di funzioni all'interno della classe.

CREATE TEMP AGGREGATE FUNCTION F(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
AS r'''

  class SumAggregator {
   constructor() {
     this.sum = 0;
   }

   update(value) {
     this.sum += value;
   }

   getSum() {
     return this.sum;
   }
  }

  export function initialState() {
   return new SumAggregator();
  }

  export function aggregate(agg, value) {
   agg.update(value);
  }

  export function merge(agg1, agg2) {
   agg1.update(agg2.getSum());
  }

  export function finalize(agg) {
   return agg.getSum();
  }

''';

--Error: getSum is not a function
SELECT F(x) AS results FROM UNNEST([1,2,3,4]) AS x;

Se aggiungi le funzioni serialize e deserialize alla query precedente, la query viene eseguita perché l'oggetto della classe SumAggregator viene convertito in un oggetto serializzabile in BigQuery e poi di nuovo in un oggetto della classe SumAggregator.

CREATE TEMP AGGREGATE FUNCTION F(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
AS r'''

  class SumAggregator {
   constructor() {
     this.sum = 0;
   }

   update(value) {
     this.sum += value;
   }

   getSum() {
     return this.sum;
   }
  }

  export function initialState() {
   return new SumAggregator();
  }

  export function aggregate(agg, value) {
   agg.update(value);
  }

  export function merge(agg1, agg2) {
   agg1.update(agg2.getSum());
  }

  export function finalize(agg) {
   return agg.getSum();
  }

  export function serialize(agg) {
   return {sum: agg.getSum()};
  }

  export function deserialize(serialized) {
   var agg = new SumAggregator();
   agg.update(serialized.sum);
   return agg;
  }

''';

SELECT F(x) AS results FROM UNNEST([1,2,3,4]) AS x;

/*-----------------*
 | results         |
 +-----------------+
 | 10.0            |
 *-----------------*/

Per scoprire di più sulle funzioni di serializzazione, consulta Funzioni di serializzazione JavaScript facoltative.

Includi variabili globali e funzioni personalizzate in una UDAF JavaScript

Il corpo della funzione JavaScript può includere codice JavaScript personalizzato, come variabili globali JavaScript e funzioni personalizzate.

Le variabili globali vengono eseguite quando il codice JavaScript viene caricato in BigQuery e prima dell'esecuzione della funzione initialState. Le variabili globali potrebbero essere utili se devi eseguire un'attività di inizializzazione una tantum che non deve essere ripetuta per ogni gruppo di aggregazione, come nel caso delle funzioni initialState, aggregate, merge e finalize.

Non utilizzare variabili globali per archiviare lo stato di aggregazione. Limita invece lo stato di aggregazione agli oggetti passati alle funzioni esportate. Utilizza le variabili globali solo per memorizzare nella cache operazioni costose che non sono specifiche di una particolare operazione di aggregazione.

Nella query seguente, la funzione SumOfPrimes calcola una somma, ma nel calcolo sono inclusi solo i numeri primi. Nel corpo della funzione JavaScript, vengono inizializzate per prime due variabili globali, primes e maxTested. Inoltre, esiste una funzione personalizzata denominata isPrime che controlla se un numero è primo.

CREATE TEMP AGGREGATE FUNCTION SumOfPrimes(x INT64)
RETURNS INT64
LANGUAGE js
AS r'''

  var primes = new Set([2]);
  var maxTested = 2;

  function isPrime(n) {
    if (primes.has(n)) {
      return true;
    }
    if (n <= maxTested) {
      return false;
    }
    for (var k = 2; k < n; ++k) {
      if (!isPrime(k)) {
        continue;
      }
      if ((n % k) == 0) {
        maxTested = n;
        return false;
      }
    }
    maxTested = n;
    primes.add(n);
    return true;
  }

  export function initialState() {
    return {sum: 0};
  }

  export function aggregate(state, x) {
    x = Number(x);
    if (isPrime(x)) {
      state.sum += x;
    }
  }

  export function merge(state, partialState) {
    state.sum += partialState.sum;
  }

  export function finalize(state) {
    return state.sum;
  }

''';

-- Call the JavaScript UDAF.
WITH numbers AS (
  SELECT * FROM UNNEST([10, 11, 13, 17, 19, 20]) AS x)
SELECT SumOfPrimes(x) AS sum FROM numbers;

/*-----*
 | sum |
 +-----+
 | 60  |
 *-----*/

Includi librerie JavaScript

Puoi estendere le tue UDAF JavaScript con l'opzione library nella clausola OPTIONS. Questa opzione consente di specificare librerie di codice esterne per la funzione UDAF JavaScript e quindi di importarle con la dichiarazione import.

Nell'esempio seguente, il codice in bar.js è disponibile per qualsiasi codice nel corpo della funzione della funzione definita dall'utente (UDAF) JavaScript:

CREATE TEMP AGGREGATE FUNCTION JsAggFn(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
OPTIONS (library = ['gs://foo/bar.js'])
AS r'''

  import doInterestingStuff from 'bar.js';

  export function initialState() {
    return ...
  }
  export function aggregate(state, x) {
    var result = doInterestingStuff(x);
    ...
  }
  export function merge(state, partial_state) {
    ...
  }
  export function finalize(state) {
    return ...;
  }

''';

Struttura JavaScript richiesta

A differenza di una funzione definita dall'utente JavaScript, in cui il corpo della funzione è un JavaScript in formato libero che viene eseguito per ogni riga, il corpo della funzione per una funzione di aggregazione definita dall'utente JavaScript è un modulo JavaScript che contiene alcune funzioni esportate integrate, che vengono richiamate in varie fasi del processo di aggregazione. Alcune di queste funzioni integrate sono obbligatorie, mentre altre sono facoltative. Puoi anche aggiungere le tue funzioni JavaScript.

Funzioni di aggregazione JavaScript obbligatorie

Puoi includere le tue funzioni JavaScript, ma il corpo della funzione JavaScript deve includere le seguenti funzioni JavaScript esportabili:

  • initialState([nonAggregateParam]): restituisce un oggetto JavaScript che rappresenta uno stato di aggregazione in cui non è stata ancora aggregata nessuna riga.

  • aggregate(state, aggregateParam[, ...][, nonAggregateParam]): aggrega una riga di dati, aggiornando lo stato per archiviare il risultato dell'aggregazione. Non restituisce un valore.

  • merge(state, partialState, [nonAggregateParam]): unisce lo stato di aggregazione partialState allo stato di aggregazione state. Questa funzione viene utilizzata quando il motore aggrega diverse sezioni di dati in parallelo e deve combinare i risultati. Non restituisce un valore.

  • finalize(finalState, [nonAggregateParam]): restituisce il risultato finale della funzione di aggregazione, dato uno stato di aggregazione finale finalState.

Per scoprire di più sulle funzioni richieste, consulta Funzioni richieste in una UDAF JavaScript.

Funzioni di serializzazione JavaScript facoltative

Se vuoi lavorare con stati di aggregazione non serializzabili, la funzione UDAF JavaScript deve fornire le funzioni serialize e deserialize. La funzione serialize converte lo stato di aggregazione in un oggetto serializzabile in BigQuery; la funzione deserialize riconverte l'oggetto serializzabile in BigQuery in uno stato di aggregazione.

  • serialize(state): restituisce un oggetto serializzabile che contiene le informazioni nello stato di aggregazione, da deserializzare tramite la funzione deserialize.

  • deserialize(serializedState): deserializza serializedState (precedentemente serializzato dalla funzione serialize) in uno stato di aggregazione che può essere passato alle funzioni serialize, aggregate, merge o finalize.

Per scoprire di più sulle funzioni di serializzazione JavaScript integrate, consulta Funzioni di serializzazione per una funzione definita dall'utente (UDAF) JavaScript.

Per scoprire come serializzare e deserializzare i dati con una UDAF JavaScript, consulta Serializza e deserializza i dati in una UDAF JavaScript.

Codifiche dei tipi SQL consentite in una UDAF JavaScript

Nelle UDAF JavaScript, i seguenti tipi di dati GoogleSQL supportati rappresentano i tipi di dati JavaScript come segue:

Tipo di dati GoogleSQL
Tipo di dati JavaScript
Note
ARRAY Array Un array di array non è supportato. Per aggirare questa limitazione, utilizza i tipi di dati Array<Object<Array>> (JavaScript) e ARRAY<STRUCT<ARRAY>> (GoogleSQL).
BIGNUMERIC Number o String Uguale a NUMERIC.
BOOL Boolean
BYTES Uint8Array
DATE Date
FLOAT64 Number
INT64 BigInt
JSON Vari tipi Il tipo di dati JSON di GoogleSQL può essere convertito in un tipo di dati JavaScript Object, Array o in un altro tipo di dati JavaScript supportato da GoogleSQL.
NUMERIC Number o String Se un valore NUMERIC può essere rappresentato esattamente come una rappresentazione in virgola mobile IEEE 754 (intervallo [-253, 253]) e non ha una parte frazionaria, viene codificato come tipo di dati Number, altrimenti viene codificato come tipo di dati String.
STRING String
STRUCT Object Ogni campo STRUCT è una proprietà denominata nel tipo di dati Object. Un campo STRUCT senza nome non è supportato.
TIMESTAMP Date Date contiene un campo di microsecondi con la frazione di microsecondi di TIMESTAMP.

Chiama una funzione definita dall'utente di aggregazione (UDAF)

Questa sezione descrive i vari modi in cui puoi chiamare una UDAF permanente o temporanea dopo averla creata in BigQuery.

Chiama una funzione di aggregazione definita dall'utente (UDAF) permanente

Puoi chiamare una UDAF permanente nello stesso modo in cui chiami una funzione di aggregazione integrata. Per saperne di più, consulta Chiamate di funzioni aggregate. Devi includere il set di dati nel percorso della funzione.

Nell'esempio seguente, la query chiama una funzione UDAF permanente denominata WeightedAverage:

SELECT my_project.my_dataset.WeightedAverage(item, weight, 2) AS weighted_average
FROM (
  SELECT 1 AS item, 2.45 AS weight UNION ALL
  SELECT 3 AS item, 0.11 AS weight UNION ALL
  SELECT 5 AS item, 7.02 AS weight
);

Viene generata una tabella con i seguenti risultati:

/*------------------*
 | weighted_average |
 +------------------+
 | 4.5              |
 *------------------*/

Chiama una funzione di aggregazione definita dall'utente (UDAF) temporanea

Puoi chiamare una funzione di aggregazione definita dall'utente temporanea nello stesso modo in cui chiami una funzione di aggregazione integrata. Per saperne di più, consulta Chiamate di funzioni aggregate.

La funzione temporanea deve essere inclusa in una query con più istruzioni o in una procedura che contiene la chiamata di funzione UDAF.

Nell'esempio seguente, la query chiama una funzione UDAF temporanea denominata WeightedAverage:

CREATE TEMP AGGREGATE FUNCTION WeightedAverage(...)

-- Temporary UDAF function call
SELECT WeightedAverage(item, weight, 2) AS weighted_average
FROM (
  SELECT 1 AS item, 2.45 AS weight UNION ALL
  SELECT 3 AS item, 0.11 AS weight UNION ALL
  SELECT 5 AS item, 7.02 AS weight
);

Viene generata una tabella con i seguenti risultati:

/*------------------*
 | weighted_average |
 +------------------+
 | 4.5              |
 *------------------*/

Ignora o includi le righe con valori NULL

Quando una funzione definita dall'utente (UDAF) JavaScript viene chiamata con l'argomento IGNORE NULLS, BigQuery ignora automaticamente le righe per le quali un argomento aggregato restituisce NULL. Queste righe vengono escluse completamente dall'aggregazione e non vengono passate alla funzione JavaScript aggregate. Quando viene fornito l'argomento RESPECT NULLS, il filtro NULL viene disattivato e ogni riga viene passata alla funzione UDAF JavaScript, indipendentemente dai valori NULL.

Se non viene fornito né l'argomento IGNORE NULLS né l'argomento RESPECT NULLS, l'argomento predefinito è IGNORE NULLS.

L'esempio seguente illustra il comportamento predefinito di NULL, il comportamento di IGNORE NULLS e il comportamento di RESPECT NULLS:

CREATE TEMP AGGREGATE FUNCTION SumPositive(x FLOAT64)
RETURNS FLOAT64
LANGUAGE js
AS r'''

  export function initialState() {
    return {sum: 0}
  }
  export function aggregate(state, x) {
    if (x == null) {
      // Use 1000 instead of 0 as placeholder for null so
      // that NULL values passed are visible in the result.
      state.sum += 1000;
      return;
    }
    if (x > 0) {
      state.sum += x;
    }
  }
  export function merge(state, partialState) {
    state.sum += partialState.sum;
  }
  export function finalize(state) {
    return state.sum;
  }

''';

-- Call the JavaScript UDAF.
WITH numbers AS (
  SELECT * FROM UNNEST([1.0, 2.0, NULL]) AS x)
SELECT
  SumPositive(x) AS sum,
  SumPositive(x IGNORE NULLS) AS sum_ignore_nulls,
  SumPositive(x RESPECT NULLS) AS sum_respect_nulls
FROM numbers;

/*-----+------------------+-------------------*
 | sum | sum_ignore_nulls | sum_respect_nulls |
 +-----+------------------+-------------------+
 | 3.0 | 3.0              | 1003.0            |
 *-----+------------------+-------------------*/

Elimina una UDAF

Questa sezione descrive i vari modi in cui puoi eliminare una funzione definita dall'utente (UDAF) permanente o temporanea dopo averla creata in BigQuery.

Elimina una funzione UDAF permanente

Per eliminare una funzione definita dall'utente (UDAF) permanente, utilizza l'istruzione DROP FUNCTION. Devi includere il set di dati nel percorso della funzione.

Nell'esempio seguente, la query elimina una UDAF permanente denominata WeightedAverage:

DROP FUNCTION IF EXISTS my_project.my_dataset.WeightedAverage;

Elimina una UDAF temporanea

Per eliminare una UDAF temporanea, utilizza l'istruzione DROP FUNCTION.

Nell'esempio seguente, la query elimina una funzione UDAF temporanea denominata WeightedAverage:

DROP FUNCTION IF EXISTS WeightedAverage;

Una funzione di aggregazione definita dall'utente temporanea scade al termine della query. Non è necessario eliminare la UDAF, a meno che tu non voglia rimuoverla in anticipo da una query con più istruzioni o da una procedura.

Elenca le UDAF

Le UDAF sono un tipo di routine. Per elencare tutte le routine in un set di dati, consulta Elenca le routine.

Suggerimenti sulle prestazioni

Se vuoi migliorare le prestazioni delle query, tieni presente quanto segue:

  • Pre-filtra l'input. L'elaborazione dei dati in JavaScript è più costosa rispetto a SQL, quindi è meglio filtrare l'input il più possibile prima in SQL.

    La seguente query è meno efficiente perché filtra l'input utilizzando x > 0 nella chiamata UDAF:

    SELECT JsFunc(x) FROM t;
    

    La seguente query è più efficiente perché prefiltra l'input utilizzando WHERE x > 0 prima della chiamata della funzione UDAF:

    SELECT JsFunc(x) FROM t WHERE x > 0;
    
  • Quando possibile, utilizza le funzioni aggregate integrate anziché JavaScript. La reimplementazione di una funzione aggregata integrata in JavaScript è più lenta della chiamata di una funzione aggregata integrata che esegue la stessa operazione.

    La seguente query è meno efficiente perché implementa una funzione UDAF:

    SELECT SumSquare(x) FROM t;
    

    La seguente query è più efficiente perché implementa una funzione integrata che produce gli stessi risultati della query precedente:

    SELECT SUM(x*x) FROM t;
    
  • Le UDAF JavaScript sono appropriate per operazioni di aggregazione più complesse, che non possono essere espresse tramite funzioni integrate.

  • Utilizza la memoria in modo efficiente. L'ambiente di elaborazione JavaScript ha una memoria limitata disponibile per ogni query. Le query UDAF JavaScript che accumulano troppi stati locali potrebbero non riuscire a causa dell'esaurimento della memoria. Presta particolare attenzione a ridurre al minimo le dimensioni degli oggetti di stato di aggregazione ed evita gli stati di aggregazione che accumulano un numero elevato di righe.

    La seguente query non è efficiente perché la funzione aggregate utilizza una quantità di memoria illimitata quando il numero di righe elaborate diventa elevato.

    export function initialState() {
      return {rows: []};
    }
    export function aggregate(state, x) {
      state.rows.push(x);
    }
    ...
    
  • Utilizza tabelle partizionate, se possibile. Le UDAF JavaScript in genere vengono eseguite in modo più efficiente quando eseguono query su una tabella partizionata rispetto a una tabella non partizionata, perché una tabella partizionata archivia i dati in molti file più piccoli rispetto a una tabella non partizionata, consentendo così un parallelismo più elevato.

Limitazioni

  • Le UDAF hanno le stesse limitazioni che si applicano alle UDF. Per maggiori dettagli, consulta Limitazioni delle funzioni definite dall'utente.

  • Solo valori letterali, parametri di ricerca e variabili di script possono essere passati come argomenti non aggregati per una UDAF.

  • L'utilizzo della clausola ORDER BY in una chiamata di funzione UDAF JavaScript non è supportato.

    SELECT MyUdaf(x ORDER BY y) FROM t; -- Error: ORDER BY is unsupported.
    

Prezzi

Le UDAF vengono fatturate utilizzando il modello di prezzi standard di BigQuery.

Quote e limiti

Le funzioni definite dall'utente aggregate hanno le stesse quote e gli stessi limiti che si applicano alle funzioni definite dall'utente. Per informazioni sulle quote delle funzioni definite dall'utente, consulta Quote e limiti.