JavaScript-UDTFs (Benutzerdefinierte Tabellenfunktionen)¶
Ähnlich wie SQL-UDTFs gibt eine JavaScript-UDTF eine Menge von Zeilen zurück, auf die in der FROM-Klausel einer Abfrage zugegriffen werden kann.
Unter diesem Thema:
Syntax¶
Eine JavaScript-UDTF wird mit derselben Syntax wie eine Standard-SQL-UDTF definiert, wobei jedoch das Schlüsselwort LANGUAGE hinzugefügt wird. Außerdem wird anstelle eines SQL-Blocks in der Funktionsdefinition der JavaScript-Code übergeben:
CREATE OR REPLACE FUNCTION <name> ( [ <arguments> ] )
RETURNS TABLE ( <output_columns> )
LANGUAGE JAVASCRIPT
AS '<javascript_code>';
Der nächste Abschnitt enthält Details zum Schreiben des Javascript-Codes
, der den Hauptteil der UDTF (auch „Definition“ genannt) enthält.
Eine ausführlichere Beschreibung der allgemeinen Syntax für alle UDFs, einschließlich JavaScript UDTFs, finden Sie unter CREATE FUNCTION.
Nutzungshinweise¶
Wenn Sie eine JavaScript-UDTF mit einer
PARTITION BY
-Klausel verwenden, darf diePARTITION BY
-Klausel nur einen Spaltenverweis, aber keinen allgemeinen Ausdruck enthalten. Folgender Befehl wird beispielsweise nicht unterstützt:SELECT * FROM udtf_table, TABLE(my_func(col1) OVER (PARTITION BY col2 * 2));
Schreiben von JavaScript-UDTFs¶
Code-Anforderungen¶
Der JavaScript-Code muss die folgenden Voraussetzungen erfüllen, damit die UDTF gültig ist:
Der Code muss ein einzelnes literales JavaScript-Objekt definieren.
Das definierte Objekt muss eine Callback-Funktion mit dem Namen
processRow
enthalten. Weitere Informationen dazu finden Sie im nächsten Abschnitt.
Wichtig
Wenn der JavaScript-Code diese Anforderungen nicht erfüllt, wird die UDTF trotzdem erstellt. Sie schlägt jedoch fehl, wenn sie in einer Abfrage aufgerufen wird.
Objekt-Callback-Funktionen¶
Über den JavaScript-Code interagiert Snowflake mit der UDTF, indem während der Ausführung der Abfrage verschiedene Callback-Funktionen aufgerufen werden. Der folgende Skeleton umreißt alle verfügbaren Callbacks und deren erwartete Signatur:
{ processRow: function (row, rowWriter, context) {/*...*/}, finalize: function (rowWriter, context) {/*...*/}, initialize: function (argumentInfo, context) {/*...*/}, }
Beachten Sie, dass nur der processRow
-Callback erforderlich ist. Der Rest ist optional.
processRow
¶
Diese Callback-Funktion wird in der Eingabebeziehung einmal für jede Zeile aufgerufen. Die Argumente für die UDTF werden im row
-Objekt übergeben. Für jedes der in der CREATE FUNCTION-DDL definierten Argumente, die zum Erstellen der UDTF verwendet wurden, gibt es eine Eigenschaft für das row
-Objekt, die überall den gleichen Namen in Großbuchstaben hat. Der Wert dieser Eigenschaft ist der Wert des Arguments für die aktuelle Zeile, der in einen JavaScript-Wert umgewandelt wird.
Das Argument rowWriter
wird vom benutzerdefinierten Code zum Erstellen von Ausgabezeilen verwendet. Für das rowWriter
-Objekt ist die Einzelfunktion writeRow
definiert. Der writeRow
-Funktion wird ein Argument übergeben, das Zeilenobjekt. Hierbei handelt es sich um eine einzelne Zeile in der Ausgabetabelle, die als JavaScript-Objekt dargestellt wird. Für jede Spalte, die in der RETURNS-Klausel der CREATE FUNCTION-DDL definiert ist, kann eine entsprechende Eigenschaft für das Zeilenobjekt definiert werden. Der Wert dieser Eigenschaft für das Zeilenobjekt wird zum Wert für die entsprechende Spalte in der Ausgabebeziehung. Ausgabespalten ohne entsprechende Eigenschaft im Zeilenobjekt haben in der Ergebnistabelle den Wert NULL.
finalize
¶
Diese Callback-Funktion wird einmal für jede Partition aufgerufen, nachdem alle Zeilen an processRow
übergeben wurden.
Sie kann verwendet werden, um einen beliebigen Status auszugeben, der möglicherweise in processRow
mit demselben Zeilenschreiber aggregiert wurde, der an processRow
übergeben wird.
initialize
¶
Diese Callback-Funktion wird einmal für jede Partition aufgerufen, bevor processRow
aufgerufen wird. Zeilen können mit dieser Methode nicht erzeugt werden.
initialize
kann verwendet werden, um einen beliebigen Status für die Ergebnisberechnung einzustellen. Außerdem werden an initialize
Metadaten zu den Argumenten der benutzerdefinierten Funktion im argumentInfo
-Objekt übergeben. argumentInfo
hat eine Eigenschaft für jede Eingabespalte, die in der DDL mit Metadaten zu dieser Spalte definiert ist. Jeder Eintrag ist ein Objekt mit den folgenden Werten:
type
: Zeichenfolge. Der Typ dieser Spalte.isConst
: Boolean. Bei „true“ ist der Wert dieser Spalte konstant (d. h. für jede Zeile der gleiche).constValue
: WennisConst
(wie oben definiert) „true“ ist, enthält dieser Eintrag den konstanten Wert der Spalte. Andernfalls lautet dieses Feldundefined
.
Allgemeine Nutzungshinweise für Callback-Funktionen¶
Alle drei Callback-Funktionen benötigen ein
context
-Objekt. Dies ist für die zukünftige Verwendung reserviert und derzeit leer.Vorsicht
Das Ändern des
context
-Objekts kann zu undefiniertem Verhalten führen.Zusätzliche Funktionen und Eigenschaften können bei Bedarf für das Objekt definiert werden, das in der UDTF verwendet werden soll.
Die Argumente für die Callback-Funktionen sind positionell und können beliebig benannt werden. Für die Zwecke dieses Themas werden die obigen Namen jedoch für die verbleibende Diskussion und die Beispiele verwendet.
Partitionen¶
JavaScript-UDTFs können mit einer Partition aufgerufen werden. Beispiel:
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expr> ORDER BY <expr>));
Eine Partition besteht aus allen Zeilen, für die die PARTITION BY
-Ausdrücke den gleichen Wert ergeben. Die Zeilen werden in der durch den Ausdruck ORDER BY definierten Reihenfolge an processRow
übergeben.
Für diese Diskussion wird eine Instanz einer JavaScript-UDTF als eine Instanz des JavaScript-Objekts definiert, das zur Darstellung der Funktion in Snowflake verwendet wird.
Zur Verbesserung der Leistung beim Aufrufen einer JavaScript-UDTF können mehrere UDTF-Instanzen parallel ausgeführt werden. Damit dies funktioniert, müssen die Zeilen in der Eingabe zwischen den verschiedenen Instanzen der Funktion aufgeteilt werden. Beim Aufrufen der JavaScript-UDTF wird die Partitionierung festgelegt:
Alle Zeilen einer Partition werden über die Funktion
processRow
an dieselbe UDTF-Instanz übergeben.initialize
undfinalize
werden jeweils einmal pro Partition aufgerufen.
Beachten Sie, dass zwischen Partitionen und UDTF-Instanzen keine Eins-zu-Eins-Beziehung besteht. Obwohl jede Partition nur von einer UDTF-Instanz verarbeitet wird, gilt die Umkehrung nicht – eine einzige UDTF-Instanz kann mehrere Partitionen verarbeiten. Daher ist es wichtig, initialize
und finalize
zu verwenden, um Partitionen speziell einzurichten und abzubauen, um beispielsweise zu vermeiden, dass angesammelte Werte von der Verarbeitung einer Partition auf die Verarbeitung einer anderen Partition übertragen werden.
Ergebnisspalten¶
In diesem Abschnitt werden die Spalten beschrieben, die von einer JavaScript-UDTF zurückgegeben werden:
Alle in der RETURNS-Klausel der CREATE FUNCTION DDL definierten Spalten werden in der Ausgabebeziehung zurückgegeben.
Alle an die UDTF übergebenen Spalten werden ebenfalls zurückgegeben.
Es wird unterschieden zwischen Zeilen, die im processRow
-Callback erzeugt werden, und Zeilen, die von finalize
erzeugt werden:
Wenn in
processRow
eine Zeile erzeugt wird, kann Snowflake diese mit einer Eingabe korrelieren, nämlich der Zeile, die alsrow
-Argument an die Funktion übergeben wird. Wenn eine gegebeneprocessRow
mehr als eine Zeile erzeugt, werden die Eingabeattribute in jede Ausgabezeile kopiert.Bei Zeilen, die in
processRow
erstellt wurden, werden in der Ausgabebeziehung alle Eingabespalten dupliziert.Im
finalize
-Callback kann Snowflake keine Korrelation zu einer einzelnen Zeile herstellen, da es keine „aktuelle Zeile“ gibt, mit der korreliert werden kann.Für Zeilen, die im
finalize
-Callback erzeugt werden, werden nur die in der PARTITION BY-Klausel verwendeten Spalten dupliziert (da diese für alle Zeilen in der aktuellen Partition gleich sind). Alle anderen Attribute sind NULL. Wenn keine PARTITION BY-Klausel angegeben ist, sind alle Eingabeattribute NULL.
Aufrufen von JavaScript-UDTFs in Abfragen¶
Keine Partitionierung¶
Dieses einfache Beispiel zeigt, wie eine UDTF aufgerufen wird. In diesem Beispiel werden Literalwerte übergeben. Die UDTF gibt lediglich die Parameter in umgekehrter Reihenfolge zurück, in der sie übergeben wurden. In diesem Beispiel wird keine Partitionierung verwendet.
SELECT * FROM TABLE(js_udtf(10.0::FLOAT, 20.0::FLOAT)); +----+----+ | Y | X | |----+----| | 20 | 10 | +----+----+
In diesem Beispiel wird eine UDTF aufgerufen, und es werden Werte aus einer anderen Tabelle übergeben. In diesem Beispiel wird die UDTF namens js_udtf
für jede Zeile in der Tabelle namens tab1
einmal aufgerufen. Bei jedem Aufruf der Funktion werden Werte aus den Spalten c1
und c2
der aktuellen Zeile übergeben. Wie oben wird die UDTF ohne eine PARTITION BY
-Klausel aufgerufen.
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2)) ;
Wenn keine Partitionierung verwendet wird, partitioniert das Snowflake-Ausführungsmodul die Eingabe selbst anhand mehrerer Faktoren, z. B. der Größe des Warehouse, das die Funktion verarbeitet, und der Kardinalität der Eingabebeziehung. In diesem Modus kann der Benutzercode keine Annahmen über Partitionen treffen. Dies ist am nützlichsten, wenn die Funktion nur Zeilen isoliert betrachten muss, um ihre Ausgabe zu erzeugen, und kein Zustand über Zeilen hinweg aggregiert wird.
Explizite Partitionierung¶
JavaScript-UDTFs können auch über eine Partition aufgerufen werden. Beispiel:
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY tab1.c3 ORDER BY tab1.c1));
Explizite Partitionierung mit einer leeren OVER
-Klausel¶
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER ());
Eine leere OVER
-Klausel bedeutet, dass jede Zeile zu derselben Partition gehört (d. h. die gesamte Eingabebeziehung ist eine Partition).
Bemerkung
Seien Sie vorsichtig, wenn Sie eine JavaScript-UDTF mit einer leeren OVER
-Klausel aufrufen, da dies Snowflake auf die Erstellung einer Instanz der Funktion beschränkt und Snowflake die Berechnung daher nicht parallelisieren kann.
Beispiele für JavaScript-UDTFs¶
Dieser Abschnitt enthält mehrere Beispiel-JavaScript-UDTFs.
Grundlegende Hello World
-Beispiele¶
Die folgende JavaScript-UDTF benötigt keine Parameter und gibt immer die gleichen Werte zurück. Sie wird hauptsächlich zu Illustrationszwecken bereitgestellt:
CREATE OR REPLACE FUNCTION HelloWorld0() RETURNS TABLE (OUTPUT_COL VARCHAR) LANGUAGE JAVASCRIPT AS '{ processRow: function f(row, rowWriter, context){ rowWriter.writeRow({OUTPUT_COL: "Hello"}); rowWriter.writeRow({OUTPUT_COL: "World"}); } }'; SELECT output_col FROM TABLE(HelloWorld0());Ausgabe:
+------------+ | OUTPUT_COL | +============+ | Hello | +------------+ | World | +------------+
Die folgende JavaScript-UDTF dient ebenfalls zur Veranschaulichung, verwendet jedoch einen Eingabeparameter. Beachten Sie, dass JavaScript die Groß-/Kleinschreibung berücksichtigt, aber SQL erzwingt bei Bezeichnern Großbuchstaben. Wenn der JavaScript-Code auf einen SQL-Parameternamen verweist, muss der JavaScript-Code Großbuchstaben verwenden.
Beachten Sie auch, dass auf Funktionsparameter über den Parameter row
in der Funktion get_params()
zugegriffen wird:
CREATE OR REPLACE FUNCTION HelloHuman(First_Name VARCHAR, Last_Name VARCHAR) RETURNS TABLE (V VARCHAR) LANGUAGE JAVASCRIPT AS '{ processRow: function get_params(row, rowWriter, context){ rowWriter.writeRow({V: "Hello"}); rowWriter.writeRow({V: row.FIRST_NAME}); // Note the capitalization and the use of "row."! rowWriter.writeRow({V: row.LAST_NAME}); // Note the capitalization and the use of "row."! } }'; SELECT V AS Greeting FROM TABLE(HelloHuman('James', 'Kirk'));Ausgabe:
+------------+ | GREETING | +============+ | Hello | +------------+ | James | +------------+ | Kirk | +------------+
Grundlegende Beispiele zur Veranschaulichung der Callback-Funktionen¶
Die folgende JavaScript-UDTF zeigt alle API-Callback-Funktionen und verschiedene Ausgabespalten. Es werden einfach alle Zeilen in der vorliegenden Form zurückgegeben und die Anzahl der Zeichen angegeben, die in jeder Partition angezeigt werden. Außerdem wird veranschaulicht, wie der Status über eine Partition mit der Referenz THIS
gemeinsam genutzt wird. Beachten Sie, dass das Beispiel einen initialize
-Callback verwendet, um den Zähler mit null zu initialisieren. Dies ist erforderlich, da eine bestimmte Funktionsinstanz zur Verarbeitung mehrerer Partitionen verwendet werden kann:
-- set up for the sample CREATE TABLE parts (p FLOAT, s STRING); INSERT INTO parts VALUES (1, 'michael'), (1, 'kelly'), (1, 'brian'); INSERT INTO parts VALUES (2, 'clara'), (2, 'maggie'), (2, 'reagan'); -- creation of the UDTF CREATE OR REPLACE FUNCTION "CHAR_SUM"(INS STRING) RETURNS TABLE (NUM FLOAT) LANGUAGE JAVASCRIPT AS '{ processRow: function (row, rowWriter, context) { this.ccount = this.ccount + 1; this.csum = this.csum + row.INS.length; rowWriter.writeRow({NUM: row.INS.length}); }, finalize: function (rowWriter, context) { rowWriter.writeRow({NUM: this.csum}); }, initialize: function(argumentInfo, context) { this.ccount = 0; this.csum = 0; }}';
Die folgende Abfrage veranschaulicht das Aufrufen der CHAR_SUM
-UDTF für die Tabelle parts
ohne Partitionierung:
SELECT * FROM parts, TABLE(char_sum(s));Ausgabe:
+--------+---------+-----+ | P | S | NUM | +--------+---------+-----+ | 1 | michael | 7 | | 1 | kelly | 5 | | 1 | brian | 5 | | 2 | clara | 5 | | 2 | maggie | 6 | | 2 | reagan | 6 | | [NULL] | [NULL] | 34 | +--------+---------+-----+Wenn keine Partitionierung angegeben ist, definiert Snowflake automatisch Partitionen. In diesem Beispiel wird aufgrund der geringen Anzahl von Zeilen nur eine Partition erstellt (d. h. es wird nur ein Aufruf von
finalize
ausgeführt). Beachten Sie, dass in der letzten Zeile für die Eingabespalten NULL-Werte verwendet werden.
Gleiche Abfrage, jedoch mit expliziter Partitionierung:
SELECT * FROM parts, TABLE(char_sum(s) OVER (PARTITION BY p));Ausgabe:
+--------+---------+-----+ | P | S | NUM | +--------+---------+-----+ | 1 | michael | 7 | | 1 | kelly | 5 | | 1 | brian | 5 | | 1 | [NULL] | 17 | | 2 | clara | 5 | | 2 | maggie | 6 | | 2 | reagan | 6 | | 2 | [NULL] | 17 | +--------+---------+-----+Dieses Beispiel wird über die Spalte
p
partitioniert, wodurch zwei Partitionen entstehen. Für jede Partition wird imfinalize
-Callback eine einzelne Zeile zurückgegeben. Dies ergibt insgesamt zwei Zeilen, die durch den NULL-Wert in der Spaltes
unterschieden werden. Dap
die PARTITION BY-Spalte ist, haben die infinalize
erstellten Zeilen den Wertp
, der die aktuelle Partition definiert.
Erweiterte Beispiele mit Tabellenwerten und anderen UDTFs als Eingabe¶
Diese einfache UDTF konvertiert einen „Bereich“ von IP-Adressen in eine vollständige Liste von IP-Adressen. Die Eingabe besteht aus den ersten 3 Segmenten der IP-Adresse (z. B. '192.168.1'
) und dann aus Anfang und Ende des Bereichs, der zum Generieren des letzten Segments (z. B. 42
und 45
) verwendet wird:
CREATE OR REPLACE FUNCTION range_to_values(PREFIX VARCHAR, RANGE_START FLOAT, RANGE_END FLOAT) RETURNS TABLE (IP_ADDRESS VARCHAR) LANGUAGE JAVASCRIPT AS $$ { processRow: function f(row, rowWriter, context) { var suffix = row.RANGE_START; while (suffix <= row.RANGE_END) { rowWriter.writeRow( {IP_ADDRESS: row.PREFIX + "." + suffix} ); suffix = suffix + 1; } } } $$; SELECT * FROM TABLE(range_to_values('192.168.1', 42::FLOAT, 45::FLOAT));Ausgabe:
+--------------+ | IP_ADDRESS | +==============+ | 192.168.1.42 | +--------------+ | 192.168.1.43 | +--------------+ | 192.168.1.44 | +--------------+ | 192.168.1.45 | +--------------+
Aufbauend auf dem vorherigen Beispiel können Sie einzelne IP-Adressen für mehr als einen Bereich berechnen. Diese nächste Anweisung erstellt eine Tabelle mit Bereichen, die zum Erweitern auf einzelne IP-Adressen verwendet werden können. Die Abfrage gibt dann die Zeilen aus der Tabelle in die range_to_values()
-UDTF ein, um die einzelnen IP-Adressen zurückzugeben:
CREATE TABLE ip_address_ranges(prefix VARCHAR, range_start INTEGER, range_end INTEGER); INSERT INTO ip_address_ranges (prefix, range_start, range_end) VALUES ('192.168.1', 42, 44), ('192.168.2', 10, 12), ('192.168.2', 40, 40) ; SELECT rtv.ip_address FROM ip_address_ranges AS r, TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv;Ausgabe:
+--------------+ | IP_ADDRESS | +==============+ | 192.168.1.42 | +--------------+ | 192.168.1.43 | +--------------+ | 192.168.1.44 | +--------------+ | 192.168.2.10 | +--------------+ | 192.168.2.11 | +--------------+ | 192.168.2.12 | +--------------+ | 192.168.2.40 | +--------------+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. Das tatsächliche Verhalten ist, dass die Funktionrange_to_values()
mit den Werten aus jeder Zeile der Tabelleip_address changes
aufgerufen wird. Mit anderen Worten: Folgendes wäre gleichbedeutend:for input_row in ip_address_ranges: output_row = range_to_values(input_row.prefix, input_row.range_start, input_row.range_end)
Das Konzept der Übergabe von Werten an eine UDTF kann auf mehrere UDTFs erweitert werden. Im nächsten Beispiel wird eine UDTF mit dem Namen fake_ipv4_to_ipv6()
erstellt, die die IPV4-Adresse in IPV6-Adressen „konvertiert“. Die Abfrage ruft die Funktion dann als Teil einer komplexeren Anweisung auf, die eine andere UDTFbeinhaltet:
-- Example UDTF that "converts" an IPV4 address to a range of IPV6 addresses. -- (for illustration purposes only and is not intended for actual use) CREATE OR REPLACE FUNCTION fake_ipv4_to_ipv6(ipv4 VARCHAR) RETURNS TABLE (IPV6 VARCHAR) LANGUAGE JAVASCRIPT AS $$ { processRow: function f(row, rowWriter, context) { rowWriter.writeRow( {IPV6: row.IPV4 + "." + "000.000.000.000"} ); rowWriter.writeRow( {IPV6: row.IPV4 + "." + "..."} ); rowWriter.writeRow( {IPV6: row.IPV4 + "." + "FFF.FFF.FFF.FFF"} ); } } $$; SELECT ipv6 FROM TABLE(fake_ipv4_to_ipv6('192.168.3.100'));Ausgabe:
+-------------------------------+ | IPV6 | +===============================+ | 192.168.3.100.000.000.000.000 | +-------------------------------+ | 192.168.3.100.... | +-------------------------------+ | 192.168.3.100.FFF.FFF.FFF.FFF | +-------------------------------+
Die folgende Abfrage verwendet die zuvor erstellten fake_ipv4_to_ipv6
- und range_to_values()
-UDTFs mit Eingabe aus der Tabelle ip_address changes
. Mit anderen Worten, sie beginnt mit einem Set von IP-Adressbereichen, konvertiert diese in einzelne IPV4-Adressen und nimmt dann jede IPV4-Adresse und „konvertiert“ diese wiederum in einen Bereich von IPV6-Adressen:
SELECT rtv6.ipv6 FROM ip_address_ranges AS r, TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv, TABLE(fake_ipv4_to_ipv6(rtv.ip_address)) AS rtv6 WHERE r.prefix = '192.168.2' -- limits the output for this example ;Ausgabe:
+------------------------------+ | IPV6 | +==============================+ | 192.168.2.10.000.000.000.000 | +------------------------------+ | 192.168.2.10.... | +------------------------------+ | 192.168.2.10.FFF.FFF.FFF.FFF | +------------------------------+ | 192.168.2.11.000.000.000.000 | +------------------------------+ | 192.168.2.11.... | +------------------------------+ | 192.168.2.11.FFF.FFF.FFF.FFF | +------------------------------+ | 192.168.2.12.000.000.000.000 | +------------------------------+ | 192.168.2.12.... | +------------------------------+ | 192.168.2.12.FFF.FFF.FFF.FFF | +------------------------------+ | 192.168.2.40.000.000.000.000 | +------------------------------+ | 192.168.2.40.... | +------------------------------+ | 192.168.2.40.FFF.FFF.FFF.FFF | +------------------------------+Beachten Sie, dass in diesem Beispiel zweimal die Join-Syntax verwendet wurde, aber keine der Operationen eine echte Verknüpfung war: Beide waren Aufrufe einer UDTF, wobei die Ausgabe einer Tabelle oder eine andere UDTF als Eingabe verwendet wurde.
Bei einem echten INNER JOIN muss die Reihenfolge nicht beachtet werden. Beispielsweise sind die folgenden Anweisungen identisch:
table1 INNER JOIN table2 ON ...
table2 INNER JOIN table1 ON ...
Die Übergabe von Werten an eine UDTF ist keine echte Verknüpfung, und bei den Operationen wird die Reihenfolge nicht beachtet. Die folgende Abfrage ist beispielsweise identisch mit dem vorherigen Beispiel, außer dass die Reihenfolge der UDTFs in der FROM-Klausel umgekehrt wird:
SELECT rtv6.ipv6 FROM ip_address_ranges AS r, TABLE(fake_ipv4_to_ipv6(rtv.ip_address)) AS rtv6, TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv WHERE r.prefix = '192.168.2' -- limits the output for this example ;Die Abfrage schlägt mit der folgenden Fehlermeldung fehl:
SQL compilation error: error line 3 at position 35 invalid identifier 'RTV.IP_ADDRESS'
Der Bezeichner
rtv.ip_address
ist ungültig, da er vor seiner Verwendung nicht definiert wurde. Bei einer echten Verknüpfung würde dies nicht passieren, aber bei der Verarbeitung von UDTFs mithilfe der Join-Syntax kann dieser Fehler möglicherweise auftreten.
Testen Sie als Nächstes eine Anweisung, die die Übergabe von Eingabewerten an eine UDTF mit einer echten Verknüpfung kombiniert. Denken Sie jedoch daran, dass die Eingabe in eine UDTF und INNER JOIN die gleiche Syntax verwenden, was verwirrend sein kann:
-- First, create a small table of IP address owners. -- This table uses only IPv4 addresses for simplicity. DROP TABLE ip_address_owners; CREATE TABLE ip_address_owners (ip_address VARCHAR, owner_name VARCHAR); INSERT INTO ip_address_owners (ip_address, owner_name) VALUES ('192.168.2.10', 'Barbara Hart'), ('192.168.2.11', 'David Saugus'), ('192.168.2.12', 'Diego King'), ('192.168.2.40', 'Victoria Valencia') ; -- Now join the IP address owner table to the IPv4 addresses. SELECT rtv.ip_address, ipo.owner_name FROM ip_address_ranges AS r, TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv, ip_address_owners AS ipo WHERE ipo.ip_address = rtv.ip_address AND r.prefix = '192.168.2' -- limits the output for this example ;Ausgabe:
+--------------+-------------------+ | IP_ADDRESS | OWNER_NAME | +==============+===================+ | 192.168.2.10 | Barbara Hart | +--------------+-------------------+ | 192.168.2.11 | David Saugus | +--------------+-------------------+ | 192.168.2.12 | Diego King | +--------------+-------------------+ | 192.168.2.40 | Victoria Valencia | +--------------+-------------------+Achtung
Das vorstehende Beispiel funktioniert wie beschrieben. Beim Kombinieren von UDTFs mit echten Verknüpfungen sollten Sie jedoch vorsichtig sein, da dies zu nicht deterministischem und/oder unerwartetem Verhalten führen kann.
Beachten Sie außerdem, dass sich dieses Verhalten in Zukunft möglicherweise ändert.