Tabellarische Java-UDFs (UDTFs)

In diesem Dokument wird erläutert, wie eine UDTF (benutzerdefinierte Tabellenfunktion) in Java geschrieben wird.

Unter diesem Thema:

Einführung

Eine UDTF kann in folgenden Sprachen geschrieben werden:

  • SQL

  • JavaScript

  • Java

Jede UDTF wird durch Ausführung der Anweisung CREATE FUNCTION erstellt, in der Folgendes spezifiziert wird:

  • Datentypen der Argumente, die an die UDTF übergeben werden.

  • Datentypen der Spalten in den Zeilen, die von der UDTF zurückgegeben werden.

  • Code, der ausgeführt werden soll, wenn die UDTF aufgerufen wird. Wenn die UDTF z. B. als JavaScript-UDTF implementiert ist, gibt die CREATE FUNCTION-Anweisung die aufzurufende JavaScript-Funktion an.

Ein UDTF-Entwickler erstellt Zeilenfunktionen (oder Zeilenmethoden), die einmal für jede Eingabezeile aufgerufen wird. Die Zeilenmethode akzeptiert null oder mehr Parameter. Für jede Eingabezeile gibt die Zeilenmethode eine Menge von null, einer oder mehreren Ausgabezeilen zurück.

Derzeit haben UDTFs, die in allen unterstützten Sprachen außer SQL geschrieben sind, die folgenden zusätzlichen Eigenschaften:

  • Zeilen können optional in Partitionen gruppiert werden. Alle Zeilen innerhalb einer Partition werden gemeinsam verarbeitet, d. h. sie werden nacheinander an dieselbe Instanz der Zeilenmethode übergeben. Die Ausgabe kann optional eine oder mehrere Zeilen enthalten, die auf gemeinsamen Merkmalen von Zeilen innerhalb der Partition basieren.

    • Für partitionierte Daten unterstützen UDTFs eine optionale Initialisierungsmethode, die einmal pro Partition aufgerufen wird, bevor die Zeilenmethode mit der Verarbeitung der ersten Zeile der Partition beginnt. Die Initialisierungsmethode kann Variablen initialisieren, die für die gesamte Partition gelten sollen.

    • Für partitionierte Daten unterstützen UDTFs eine optionale Finalisierungsmethode, die einmal pro Partition aufgerufen wird, nachdem die Zeilenmethode die Verarbeitung der letzten Zeile der Partition beendet hat. Die Finalisierungsmethode ermöglicht dem Code die Rückgabe von null, einer oder mehreren Ausgabezeilen, die nicht an eine bestimmte Eingabezeile gebunden sind. Die Ausgabezeilen können sein:

      • Zeilen, die zu der bereits für die Partition erstellten Ausgabe hinzugefügt werden.

      • Eine oder mehrere Zeilen, die die Partition zusammenfassen. In diesem Fall hat die Zeilenmethode möglicherweise keine Eingabezeilen generiert, sondern lediglich Berechnungen durchgeführt, die von der Finalisierungsmethode beim Generieren der Ausgabezeilen verwendet werden.

    Die Angabe der Spalte, auf der die Partitionierung der Zeilen erfolgen soll, erfolgt in der SQL-Anweisung, mit der die UDTF-Anweisung aufgerufen wird.

    Weitere Informationen zum Partitionieren finden Sie unter Tabellenfunktionen und Partitionen.

Für Java-UDTFs werden die Initialisierungsmethode, die Zeilenmethode und die Finalisierungsmethode wie unten beschrieben implementiert:

  • Der Initialisierer wird durch das Schreiben eines Null-Argument-Konstruktors implementiert.

  • Die Zeilenmethode heißt process.

  • Der Finalizer ist als Null-Argument-Methode mit dem Namen endPartition implementiert.

Jede Java-UDTF erfordert eine Handler-Klasse, die den Konstruktor, process und endPartition definiert. Weitere Informationen dazu finden Sie unter Java-Klassen für UDTFs (unter diesem Thema).

Jede Java-UDTF erfordert auch eine Ausgabezeilenklasse, die die Java-Datentypen der Spalten der Ausgabezeile(n) angibt, die von der Handler-Klasse generiert werden. Weitere Informationen dazu finden Sie unter Die Ausgabezeilenklasse (unter diesem Thema).

Bemerkung

Tabellarische Funktionen (UDTFs) haben einen Grenzwert von 500 Eingabeargumenten und 500 Ausgabespalten.

Java-Klassen für UDTFs

Die Hauptkomponenten von UDTF sind die Handler-Klasse und die Ausgabezeilenklasse.

Die Handler-Klasse

Snowflake interagiert mit der UDTF in erster Linie über Aufrufe der folgenden Methoden der Handler-Klasse:

  • Initialisierer (der Konstruktor)

  • Zeilenmethode (process)

  • Finalisierungsmethode (endPartition)

Die Handler-Klasse kann zusätzliche Methoden enthalten, die zur Unterstützung dieser drei Methoden erforderlich sind.

Die Handler-Klasse enthält auch eine Methode getOutputClass, die weiter unten beschrieben wird.

Das Auslösen einer Ausnahme durch eine beliebige Methode der Handler-Klasse (oder der Ausgabezeilenklasse) führt zum Abbruch der Verarbeitung. Die Abfrage, mit der die UDTF aufgerufen wurde, schlägt mit einer Fehlermeldung fehl.

Der Konstruktor

Eine Handler-Klasse kann einen Konstruktor haben, der keine Argumente annimmt.

Der Konstruktor wird einmal für jede Partition aufgerufen, bevor process aufgerufen wird.

Der Konstruktor kann keine Ausgabezeilen erzeugen.

Verwenden Sie den Konstruktor, um den Zustand der Partition zu initialisieren. Dieser Zustand kann von den Methoden process und endPartition verwendet werden. Der Konstruktor eignet sich auch gut bei einer langwierigen Initialisierung, die nur einmal pro Partition und nicht einmal pro Zeile erfolgen muss.

Der Konstruktor ist optional.

Die process-Methode

Die process-Funktion wird einmal für jede Zeile der Eingabepartition aufgerufen.

Die an die UDTF übergebenen Argumente werden an process übergeben. Die Werte der Argumente werden von SQL-Datentypen in Java-Datentypen konvertiert. (Weitere Informationen zur Zuordnung von SQL- zu Java-Datentypen finden Sie unter Zuordnung von SQL-Java-Datentypen für Parameter und Rückgabetypen.)

Die Parameternamen der process-Methode können beliebige gültige Java-Bezeichner sein. Die Namen müssen nicht mit den in der CREATE FUNCTION-Anweisung angegebenen Namen übereinstimmen.

Bei jedem Aufruf von process können null, eine oder mehrere Zeilen zurückgeben werden.

Der von der process-Methode zurückgegebene Datentyp muss Stream<OutputRow> sein, wobei „Stream“ in java.util.stream.Stream definiert ist und OutputRow der Klassenname der Ausgabezeile ist. Im folgenden Beispiel wird eine einfache process-Methode verwendet, die lediglich die Eingabe über einen Stream zurückgibt:

import java.util.stream.Stream;

...

public Stream<OutputRow> process(String v) {
  return Stream.of(new OutputRow(v));
}

...

Wenn die Methode process keinen Zustand des Objekts beibehält oder verwendet (z. B. wenn die Methode nur ausgewählte Eingabezeilen von der Ausgabe ausschließen soll), können Sie die Methode static deklarieren. Wenn die process-Methode static ist und die Handler-Klasse keinen Konstruktor oder eine nicht statische endPartition-Methode hat, übergibt Snowflake jede Zeile direkt an die statische process-Methode, ohne eine Instanz der Handler-Klasse zu erzeugen.

Wenn Sie eine Eingabezeile überspringen und die nächste Zeile verarbeiten müssen (z. B. wenn Sie die Eingabezeilen validieren), geben Sie ein leeres Stream-Objekt zurück. Die nachstehende process-Methode gibt zum Beispiel nur die Zeilen zurück, für die number eine positive ganze Zahl ist. Wenn number nicht positiv ist, gibt die Methode ein leeres Stream-Objekt zurück, um die aktuelle Zeile zu überspringen und mit der Verarbeitung der nächsten Zeile fortzufahren.

public Stream<OutputRow> process(int number) {
  if (inputNumber < 1) {
    return Stream.empty();
  }
  return Stream.of(new OutputRow(number));
}

Wenn process einen Null-Stream zurückgibt, wird die Verarbeitung abgebrochen. (Die endPartition-Methode wird auch dann aufgerufen, wenn ein Null-Stream zurückgegeben wird.)

Diese Methode ist veraltet.

Die endPartition-Methode

Diese Methode kann verwendet werden, um Ausgabezeilen zu erzeugen, die auf beliebigen, in process aggregierten Zustandsinformationen basieren.

Diese Methode wird einmal für jede Partition aufgerufen, nachdem alle Zeilen in dieser Partition an process übergeben wurden.

Bemerkung

Wenn der Benutzer die Daten nicht explizit partitioniert, dann partitioniert Snowflake die Daten implizit. Weitere Informationen dazu finden Sie unter Partitionen.

Diese Methode kann null, eine oder mehrere Zeilen ausgeben.

Diese Methode ist optional.

Die getOutputClass-Methode

Diese Methode gibt Informationen über die Ausgabezeilenklasse zurück. Die Ausgabezeilenklasse enthält Informationen zu den Datentypen der zurückgegebenen Zeile.

Die Ausgabezeilenklasse

Snowflake verwendet die Ausgabezeilenklasse, um Konvertierungen zwischen Java-Datentypen und SQL-Datentypen zu spezifizieren.

Wenn eine Java-UDTF eine Zeile zurückgibt, muss der Wert jeder Spalte der Zeile vom Java-Datentyp in den entsprechenden SQL-Datentyp konvertiert werden. Die SQL-Datentypen werden in der RETURNS-Klausel der CREATE FUNCTION-Anweisung angegeben. Die Zuordnung von Java- zu SQL-Datentypen ist jedoch nicht 1:1, sodass Snowflake den Java-Datentyp für jede zurückgegebene Spalte kennen muss. (Weitere Informationen zur Zuordnung von SQL- zu Java-Datentypen finden Sie unter Zuordnung von SQL-Java-Datentypen für Parameter und Rückgabetypen.)

Eine Java-UDTF spezifiziert die Java-Datentypen der Ausgabespalten durch die Definition einer Ausgabezeilenklasse. Jede von der UDTF zurückgegebene Zeile wird als Instanz der Ausgabezeilenklasse zurückgegeben. Jede Instanz der Ausgabezeilenklasse enthält ein öffentliches Feld für jede Ausgabespalte. Snowflake liest die Werte der öffentlichen Felder aus jeder Instanz der Ausgabezeilenklasse, konvertiert die Java-Werte in SQL-Werte und konstruiert eine SQL-Ausgabezeile, die diese Werte enthält.

Die Werte in jeder Instanz der Ausgabezeilenklasse werden durch den Aufruf des Konstruktors der Ausgabezeilenklasse festgelegt. Der Konstruktor nimmt Parameter entgegen, die den Ausgabespalten entsprechen, und setzt dann die öffentlichen Felder auf diese Parameter.

Der folgende Code definiert ein Beispiel für eine Ausgabezeilenklasse:

class OutputRow {

  public String name;
  public int id;

  public OutputRow(String pName, int pId) {
    this.name = pName;
    this.id = pId
  }

}

Die von dieser Klasse angegebenen öffentlichen Variablen müssen mit den in der RETURNS TABLE (...)-Klausel der CREATE FUNCTION-Anweisung angegebenen Spalten übereinstimmen. Beispielsweise entspricht die obige OutputRow-Klasse der folgenden RETURNS-Klausel:

CREATE FUNCTION F(...)
    RETURNS TABLE(NAME VARCHAR, ID INTEGER)
    ...

Wichtig

Der Abgleich zwischen den SQL-Spaltennamen und den Namen der öffentlichen Java-Felder in der Ausgabezeilenklasse erfolgt ohne Berücksichtigung der Groß-/Kleinschreibung. Im oben gezeigten Java- und SQL-Code entspricht beispielsweise das Java-Feld mit dem Namen id der SQL-Spalte mit dem Namen ID.

Die Ausgabezeilenklasse wird wie folgt verwendet:

  • Die Handler-Klasse verwendet die Ausgabezeilenklasse, um den Rückgabetyp der process-Methode und der endPartition-Methode anzugeben. Die Handler-Klasse verwendet auch die Ausgabezeilenklasse, um die zurückgegebenen Werte zu konstruieren. Beispiel:

    public Stream<OutputRow> process(String v) {
      ...
      return Stream.of(new OutputRow(...));
    }
    
    public Stream<OutputRow> endPartition() {
      ...
      return Stream.of(new OutputRow(...));
    }
    
  • Die Ausgabezeilenklasse wird auch in der getOutputClass-Methode der Handler-Klasse verwendet, die eine statische Methode ist, mit der Snowflake aufgerufen wird, um die Java-Datentypen der Ausgabe zu erfahren:

    public static Class getOutputClass() {
      return OutputRow.class;
    }
    

Das Auslösen einer Ausnahme durch eine beliebige Methode der Ausgabezeilenklasse (oder der Handler-Klasse) führt zum Abbruch der Verarbeitung. Die Abfrage, mit der die UDTF aufgerufen wurde, schlägt mit einer Fehlermeldung fehl.

Zusammenfassung der Anforderungen

Der Java-Code der UDTFmuss die folgenden Anforderungen erfüllen:

  • Der Code muss eine Ausgabezeilenklasse definieren.

  • Die Handler-Klasse der UDTF muss eine öffentliche Methode namens process enthalten, die einen „Stream“ der <Ausgabezeilenklasse> zurückgibt, wobei „Stream“ in java.util.stream.Stream definiert ist.

  • Die Handler-Klasse der UDTF muss eine öffentliche statische Methode namens getOutputClass definieren, die <Ausgabezeilenklasse>.class zurückgeben muss.

Wenn der Java-Code diese Anforderungen nicht erfüllt, schlägt entweder die Erstellung oder die Ausführung der UDTF fehl:

  • Wenn die Sitzung zum Zeitpunkt der Ausführung der CREATE FUNCTION-Anweisung über ein aktives Warehouse verfügt, erkennt Snowflake mögliche Verstöße beim Erstellen der Funktion.

  • Wenn die Sitzung zum Zeitpunkt der Ausführung der CREATE FUNCTION-Anweisung kein aktives Warehouse hat, erkennt Snowflake mögliche Verstöße beim Aufrufen der Funktion.

Aufrufen einer Java-UDTF

Rufen Sie eine Java-UDTF so wie jede andere Tabellenfunktion auf. Wenn Sie eine UDTF in der FROM-Klausel einer Abfrage aufrufen, geben Sie den Namen und die Argumente der UDTF innerhalb der Klammern an, die dem Schlüsselwort TABLE folgen.

Verwenden Sie beim Aufrufen einer UDTF das Schlüsselwort TABLE in etwa wie folgt:

SELECT ...
  FROM TABLE ( udtf_name (udtf_arguments) )

Mit der folgenden Anweisung wird beispielsweise eine Tabellenfunktion aufgerufen und ein DATE-Literal übergeben:

select ...
  from table(my_java_udtf('2021-01-16'::DATE));

Das Argument einer Tabellenfunktion kann auch ein Ausdruck sein, nicht nur ein Literal. Eine Tabellenfunktion kann zum Beispiel unter Verwendung einer Spalte aus einer Tabelle aufgerufen werden. Einige Beispiele finden Sie unten, einschließlich im Abschnitt Beispiele.

Weitere allgemeine Informationen zu Tabellenfunktionen finden Sie unter Tabellenfunktion.

Tabellenfunktionen und Partitionen

Bevor Zeilen an Tabellenfunktionen übergeben werden, können die Zeilen in Partitionen gruppiert werden. Die Partitionierung hat zwei wesentliche Vorteile:

  • Die Partitionierung ermöglicht Snowflake eine Aufteilung des Workloads, wodurch sich die Parallelisierung und damit die Leistung verbessern lässt.

  • Die Partitionierung ermöglicht es Snowflake, alle Zeilen mit einem gemeinsamen Merkmal als Gruppe zu verarbeiten. Sie können Ergebnisse zurückgeben, die auf allen Zeilen einer Gruppe basieren, nicht nur auf einzelnen Zeilen.

Sie können beispielsweise Daten zu Aktienkursen in eine Gruppe pro Aktie partitionieren. Alle Aktienkurse eines Unternehmens können zusammen analysiert werden, während die Aktienkurse jedes Unternehmens unabhängig von jedem anderen Unternehmen analysiert werden können.

Daten können explizit oder implizit partitioniert werden.

Explizite Partitionierung

Explizite Partitionierung in mehrere Gruppen

Mit der folgenden Anweisung wird die UDTF namens my_udtf auf einzelnen Partitionen aufgerufen. Jede Partition enthält alle Zeilen, für die der PARTITION BY-Ausdruck denselben Wert ergibt (z. B. dieselbe Firma oder dasselbe Aktiensymbol).

SELECT *
    FROM stocks_table AS st,
         TABLE(my_udtf(st.symbol, st.transaction_date, st.price) OVER (PARTITION BY st.symbol))

Explizite Partitionierung in eine einzige Gruppe

Mit der folgenden Anweisung wird die UDTF namens my_udtf auf einer Partition aufgerufen. Die Klausel PARTITION BY <Konstante> (in diesem Fall PARTITION BY 1) bringt alle Zeilen in dieselbe Partition.

SELECT *
    FROM stocks_table AS st,
         TABLE(my_udtf(st.symbol, st.transaction_date, st.price) OVER (PARTITION BY 1))

Ein vollständigeres und realistischeres Beispiel finden Sie unter Beispiele für den Aufruf von Java-UDTFs in Abfragen, insbesondere im Unterabschnitt Einzelne Partition.

Sortieren von Zeilen für Partitionen

Wenn die Zeilen jeder Partition in einer bestimmten Reihenfolge verarbeiten werden sollen, fügen Sie eine ORDER BY-Klausel hinzu. Diese weist Snowflake an, die Zeilen in der angegebenen Reihenfolge an die Handler-Zeilenmethode zu übergeben.

Wenn Sie z. B. den gleitenden Durchschnitt eines Aktienkurses über die Zeit berechnen möchten, dann sortieren Sie die Aktienkurse nach Zeitstempel (und führen eine Partitionierung nach Aktiensymbol aus). Im folgenden Beispiel wird gezeigt, wie Sie dies tun können:

SELECT *
     FROM stocks_table AS st,
          TABLE(my_udtf(st.symbol, st.transaction_date, st.price) OVER (PARTITION BY st.symbol ORDER BY st.transaction_date))

Eine OVER-Klausel kann eine ORDER BY-Klausel auch ohne eine PARTITION BY-Klausel enthalten.

Denken Sie daran, dass das Einfügen einer ORDER BY-Klausel innerhalb einer OVER ()-Klausel nicht dasselbe ist wie das Einfügen einer ORDER BY-Klausel auf der äußersten Ebene der Abfrage. Wenn Sie möchten, dass die gesamten Abfrageergebnisse sortiert werden, benötigen Sie eine separate ORDER BY-Klausel. Beispiel:

SELECT *
    FROM stocks_table AS st,
         TABLE(my_udtf(st.symbol, st.transaction_date, st.price) OVER (PARTITION BY st.symbol ORDER BY st.transaction_date))
    ORDER BY st.symbol, st.transaction_date, st.transaction_time;

Nutzungshinweise für die explizite Partitionierung

Wenn Sie eine UDTF mit einer PARTITION BY-Klausel verwenden, darf die PARTITION BY-Klausel nur einen Spaltenverweis oder ein Literal, aber keinen allgemeinen Ausdruck enthalten. Folgender Befehl wird beispielsweise nicht unterstützt:

SELECT * FROM udtf_table, TABLE(my_func(col1) OVER (PARTITION BY udtf_table.col2 * 2));   -- NO!

Implizite Partitionierung

Wenn eine Tabellenfunktion keine explizite Partitionierung der Zeilen mithilfe der PARTITION BY-Klausel vornimmt, partitioniert Snowflake die Zeilen in der Regel implizit, um die Parallelverarbeitung zur Leistungssteigerung zu nutzen.

Die Anzahl der Partitionen richtet sich in der Regel nach Faktoren wie der Größe des Warehouses, über das die Funktion ausgeführt wird, und der Kardinalität der Eingabebeziehung. Die Zeilen werden in der Regel auf Grundlage von Faktoren wie dem physischen Speicherort der Zeilen (z. B. durch Mikropartitionierung) bestimmten Partitionen zugeordnet, sodass die Gruppierung nicht sinnvoll ist.

Bei Ausführung mit impliziter Partitionierung kann der Benutzercode keine Annahmen über Partitionen treffen. Eine Ausführung mit impliziter Partitionierung ist vor allem dann sinnvoll, wenn die UDTF zum Erzeugen der Ausgabe Zeilen nur isoliert betrachten muss und ein Zustand nicht über Zeilen hinweg aggregiert wird. In diesem Fall braucht der Code wahrscheinlich weder einen Konstruktor noch eine endPartition-Methode.

Wenn die UDF eine Finalisierungsmethode enthält (z. B. endPartition für Java-UDFs), wird der Finalizer für jede Partition aufgerufen, unabhängig davon, ob die Daten explizit oder implizit partitioniert wurden. Wenn die Daten nicht sinnvoll partitioniert sind, ist die Ausgabe des Finalizers möglicherweise nicht sinnvoll.

Nutzungshinweise für die Partitionierung

  • Zur Verbesserung der Verarbeitungsleistung führt Snowflake normalerweise mehrere Instanzen des Handler-Codes der UDTF parallel aus. Jede Partition von Zeilen wird an eine einzelne Instanz der UDTF übergeben.

    Obwohl jede Partition nur von einer einzigen UDTF-Instanz verarbeitet wird, gilt die Umkehrung nicht: Eine UDTF-Instanz kann mehrere Partitionen nacheinander verarbeiten. Daher ist es wichtig, den Initialisierer und den Finalizer zur Initialisierung und Bereinigung für jede Partition zu verwenden, um zu vermeiden, dass akkumulierte Werte von der Verarbeitung einer Partition zur Verarbeitung einer anderen Partition übertragen werden.

Beispiele für den Aufruf von Java-UDTFs in Abfragen

Aufruf ohne explizite Partitionierung

Im folgenden Beispiel wird das Erstellen einer UDTF gezeigt: In diesem Beispiel werden zwei Kopien jeder Eingabe zurückgegeben, und für jede Partition wird eine zusätzliche Zeile zurückgegeben.

create function return_two_copies(v varchar)
returns table(output_value varchar)
language java
handler='TestFunction'
target_path='@~/TestFunction.jar'
as
$$

  import java.util.stream.Stream;

  class OutputRow {

    public String output_value;

    public OutputRow(String outputValue) {
      this.output_value = outputValue;
    }

  }


  class TestFunction {

    String myString;

    public TestFunction()  {
      myString = "Created in constructor and output from endPartition()";
    }

    public static Class getOutputClass() {
      return OutputRow.class;
    }

    public Stream<OutputRow> process(String inputValue) {
      // Return two rows with the same value.
      return Stream.of(new OutputRow(inputValue), new OutputRow(inputValue));
    }

    public Stream<OutputRow> endPartition() {
      // Returns the value we initialized in the constructor.
      return Stream.of(new OutputRow(myString));
    }

  }

$$;

Dieses Beispiel zeigt, wie eine UDTF aufgerufen wird. Um dieses Beispiel einfach zu halten, übergibt die Anweisung keine Spalte sondern einen Literalwert und vermeidet die OVER()-Klausel.

SELECT output_value
   FROM TABLE(return_two_copies('Input string'));
+-------------------------------------------------------+
| OUTPUT_VALUE                                          |
|-------------------------------------------------------|
| Input string                                          |
| Input string                                          |
| Created in constructor and output from endPartition() |
+-------------------------------------------------------+

In diesem Beispiel wird die UDTF mit Werten aufgerufen, die aus einer anderen Tabelle gelesen wurden. Jedes Mal, wenn die process-Methode aufgerufen wird, wird ihr ein Wert aus der Spalte city_name der aktuellen Zeile der Tabelle cities_of_interest übergeben. Wie oben wird die UDTF ohne explizite OVER()-Klausel aufgerufen.

Erstellen Sie eine einfache Tabelle, die als Quelle für Eingaben verwendet werden kann:

CREATE TABLE cities_of_interest (city_name VARCHAR);
INSERT INTO cities_of_interest (city_name) VALUES
    ('Toronto'),
    ('Warsaw'),
    ('Kyoto');

Rufen Sie die Java-UDTF auf:

SELECT city_name, output_value
   FROM cities_of_interest,
       TABLE(return_two_copies(city_name))
   ORDER BY city_name, output_value;
+-----------+-------------------------------------------------------+
| CITY_NAME | OUTPUT_VALUE                                          |
|-----------+-------------------------------------------------------|
| Kyoto     | Kyoto                                                 |
| Kyoto     | Kyoto                                                 |
| Toronto   | Toronto                                               |
| Toronto   | Toronto                                               |
| Warsaw    | Warsaw                                                |
| Warsaw    | Warsaw                                                |
| NULL      | Created in constructor and output from endPartition() |
+-----------+-------------------------------------------------------+

Achtung

In diesem Beispiel ist die in der FROM-Klausel verwendete Syntax identisch mit der Syntax einer inneren Verknüpfung (d. h. FROM t1, t2). Die ausgeführte Operation ist jedoch keine echte innere Verknüpfung. Tatsächlich ist das Verhalten so, dass die Funktion mit den Werten aus jeder Zeile der Tabelle aufgerufen wird. Angenommen, die folgende FROM-Klausel ist wie folgt:

from cities_of_interest, table(f(city_name))

Das Verhalten würde dann folgendem Pseudocode entsprechen:

for city_name in cities_of_interest:
    output_row = f(city_name)

Der Beispielabschnitt in der Dokumentation zu JavaScript-UDTFs enthält komplexere Beispiele für Abfragen, die UDTFs mit Werten aus Tabellen aufrufen.

Wenn die Anweisung die Partitionierung nicht explizit angibt, verwendet das Snowflake-Ausführungsmodul die implizite Partitionierung.

Wenn es nur eine einzige Partition gibt, wird die endPartition-Methode nur einmal aufgerufen, und die Ausgabe der Abfrage enthält nur eine Zeile mit dem Wert Created in constructor and output from endPartition(). Wenn die Daten bei verschiedenen Ausführungen der Anweisung in eine unterschiedliche Anzahl von Partitionen gruppiert werden, wird die endPartition-Methode unterschiedlich oft aufgerufen, und die Ausgabe enthält eine unterschiedliche Anzahl von Kopien dieser Zeile.

Weitere Informationen dazu finden Sie unter implizite Partitionierung.

Aufruf mit expliziter Partitionierung

Java-UDTFs können auch durch explizite Partitionierung aufgerufen werden.

Mehrere Partitionen

Im folgenden Beispiel werden dieselbe UDTF und die zuvor erstellte Tabelle verwendet. Im Beispiel werden die Daten nach „city_name“ partitioniert.

SELECT city_name, output_value
   FROM cities_of_interest,
       TABLE(return_two_copies(city_name) OVER (PARTITION BY city_name))
   ORDER BY city_name, output_value;
+-----------+-------------------------------------------------------+
| CITY_NAME | OUTPUT_VALUE                                          |
|-----------+-------------------------------------------------------|
| Kyoto     | Created in constructor and output from endPartition() |
| Kyoto     | Kyoto                                                 |
| Kyoto     | Kyoto                                                 |
| Toronto   | Created in constructor and output from endPartition() |
| Toronto   | Toronto                                               |
| Toronto   | Toronto                                               |
| Warsaw    | Created in constructor and output from endPartition() |
| Warsaw    | Warsaw                                                |
| Warsaw    | Warsaw                                                |
+-----------+-------------------------------------------------------+

Einzelne Partition

Im folgenden Beispiel werden dieselbe UDTF und Tabelle verwendet, die zuvor erstellt wurden, und partitioniert die Daten durch eine Konstante, die Snowflake dazu zwingt, nur eine einzige Partition zu verwenden:

SELECT city_name, output_value
   FROM cities_of_interest,
       TABLE(return_two_copies(city_name) OVER (PARTITION BY 1))
   ORDER BY city_name, output_value;
+-----------+-------------------------------------------------------+
| CITY_NAME | OUTPUT_VALUE                                          |
|-----------+-------------------------------------------------------|
| Kyoto     | Kyoto                                                 |
| Kyoto     | Kyoto                                                 |
| Toronto   | Toronto                                               |
| Toronto   | Toronto                                               |
| Warsaw    | Warsaw                                                |
| Warsaw    | Warsaw                                                |
| NULL      | Created in constructor and output from endPartition() |
+-----------+-------------------------------------------------------+

Beachten Sie, dass nur eine Kopie der Meldung Created in constructor and output from endPartition() in der Ausgabe enthalten ist, was darauf hinweist, dass endPartition nur einmal aufgerufen wurde.

Verarbeiten sehr großer Eingaben (z. B. großer Dateien)

In einigen Fällen benötigt eine UDTF sehr viel Arbeitsspeicher, um jede Eingabezeile verarbeiten zu können. Eine UDTF könnte zum Beispiel eine Datei lesen und verarbeiten, die zu groß ist, um in den Arbeitsspeicher zu passen.

Um große Dateien in einer UDF oder UDTF zu verarbeiten, verwenden Sie die Klasse SnowflakeFile oder InputStream. Weitere Informationen dazu finden Sie unter Verarbeiten von unstrukturierten Daten mit Java-UDFs oder Java-UDTFs.

Verwenden einer Tabelle oder UDTF als Eingabe für eine UDTF

Die Eingabe für eine Tabellenfunktion kann aus einer Tabelle oder aus einer anderen UDTF stammen, wie unter Verwenden einer Tabelle als Eingabe einer Tabellenfunktion dokumentiert.

Das folgende Beispiel zeigt, wie eine Tabelle als Eingabe der Java-UDTF split_file_into_words verwendet werden kann:

create table file_names (file_name varchar);
insert into file_names (file_name) values ('sample.txt'),
                                          ('sample_2.txt');

select f.file_name, w.word
   from file_names as f, table(split_file_into_words(f.file_name)) as w;

Die Ausgabe sollte ungefähr wir folgt aussehen:

+-------------------+------------+
| FILE_NAME         | WORD       |
+-------------------+------------+
| sample_data.txt   | some       |
| sample_data.txt   | words      |
| sample_data_2.txt | additional |
| sample_data_2.txt | words      |
+-------------------+------------+

Die imports-Klausel der Java-UDTF muss den Namen und den Pfad von jeder Datei angeben, die an die UDTF übergeben wird. Beispiel:

create function split_file_into_words(inputFileName string)
    ...
    imports = ('@inline_jars/sample.txt', '@inline_jars/sample_2.txt')
    ...

Jede Datei muss bereits in einen Stagingbereich (in diesem Fall in den Stagingbereich mit dem Namen @inline_jars) kopiert worden sein, bevor die UDTF die Datei liest.

Ein Beispiel für die Verwendung einer UDTF als Eingabe einer anderen UDTF finden Sie in der JavaScript-UDTF-Dokumentation unter Erweiterte Beispiele mit Tabellenwerten und anderen UDTFs als Eingabe.

Zurück zum Anfang