UDFs JavaScript tabulaires (UDTFs)¶
Vous pouvez Ă©crire le gestionnaire dâune fonction de table dĂ©finie par lâutilisateur (UDTF) en JavaScript.
Votre code de gestionnaire traite les lignes reçues dans lâappel de lâUDTF et renvoie un rĂ©sultat sous forme de tableau. Les lignes reçues sont partitionnĂ©es, soit implicitement par Snowflake, soit explicitement dans la syntaxe de lâappel de fonction. Vous utilisez les fonctions de rappel que vous Ă©crivez pour traiter des lignes individuelles ainsi que les partitions dans lesquelles elles sont regroupĂ©es.
Le code JavaScript doit rĂ©pondre aux exigences suivantes pour que lâUDTF soit valide :
Le code doit définir un seul objet JavaScript littéral.
Lâobjet dĂ©fini doit inclure une fonction de rappel appelĂ©e
processRow()
. Pour plus dâinformations, voir Fonctions de rappel dâobjets.
Important
Si le code JavaScript ne rĂ©pond pas Ă ces exigences, lâUDTF sera quand mĂȘme créé ; cependant, il Ă©chouera lorsquâil sera appelĂ© dans une requĂȘte.
Note
Les fonctions tabulaires (UDTFs) ont une limite de 500 arguments dâentrĂ©e et 500 colonnes de sortie.
Fonctions de rappel dâobjets¶
GrĂące au code JavaScript, Snowflake interagit avec lâUDTF en appelant diverses fonctions de rappel pendant lâexĂ©cution de la requĂȘte. Le squelette suivant dĂ©crit toutes les fonctions de rappel disponibles et leurs signatures attendues :
{
processRow: function (row, rowWriter, context) {/*...*/},
finalize: function (rowWriter, context) {/*...*/},
initialize: function (argumentInfo, context) {/*...*/},
}
Notez que seule processRow()
est obligatoire ; les autres fonctions sont facultatives.
processRow()
¶
Cette fonction de rappel est appelĂ©e une fois pour chaque ligne de la relation dâentrĂ©e. Les arguments de lâprocessRow()
sont transmis dans lâobjet row
. Pour chacun des arguments dĂ©finis dans lâinstruction CREATE FUNCTION utilisĂ© pour crĂ©er lâUDTF, il y a une propriĂ©tĂ© sur lâobjet row
avec le mĂȘme nom en majuscules. La valeur de cette propriĂ©tĂ© est la valeur de lâargument de la ligne courante. (La valeur est convertie en une valeur JavaScript).
Lâargument rowWriter
est utilisĂ© par le code fourni par lâutilisateur pour produire des lignes de sortie. Lâobjet rowWriter
définit une seule fonction, writeRow()
. La fonction writeRow()
prend un argument, lâobjet ligne, qui est une ligne simple dans la table de sortie reprĂ©sentĂ©e comme un objet JavaScript. Pour chaque colonne dĂ©finie dans la clause RETURNS de la commande CREATE FUNCTION, une propriĂ©tĂ© correspondante peut ĂȘtre dĂ©finie sur lâobjet de ligne. La valeur de cette propriĂ©tĂ© sur lâobjet de ligne deviendra la valeur de la colonne correspondante dans la relation de sortie. Toute colonne de sortie sans propriĂ©tĂ© correspondante sur lâobjet de ligne aura la valeur NULL dans la table des rĂ©sultats.
finalize()
¶
Cette fonction de rappel finalize()
est appelée une fois, aprÚs que toutes les lignes ont été transmises dans processRow()
. (Si les données sont regroupées en partitions, alors finalize()
est appelé une fois pour chaque partition, aprÚs que toutes les lignes de cette partition ont été transmises à processRow()
).
Cette fonction de rappel peut ĂȘtre utilisĂ©e pour sortir nâimporte quel Ă©tat qui peut avoir Ă©tĂ© agrĂ©gĂ© dans processRow()
en utilisant le mĂȘme gĂ©nĂ©rateur de lignes rowWriter
que celui qui est transmis dans processRow()
.
Note
Bien que Snowflake prenne en charge les grandes partitions avec des dĂ©lais dâexpiration dĂ©finis pour les traiter avec succĂšs, les partitions particuliĂšrement grandes peuvent entraĂźner des expirations (par exemple lorsque finalize
prend trop de temps Ă se terminer). Veuillez contacter le support Snowflake si vous avez besoin dâajuster le seuil dâexpiration pour des scĂ©narios dâutilisation spĂ©cifiques.
initialize()
¶
Cette fonction de rappel est appelée une fois pour chaque partition avant tout appel de processRow()
.
Utilisez initialize()
pour configurer nâimporte quel Ă©tat nĂ©cessaire pendant le calcul du rĂ©sultat.
Le paramĂštre argumentInfo
de la fonction initialize()
contient des mĂ©tadonnĂ©es sur les arguments de la fonction dĂ©finie par lâutilisateur. Par exemple, si lâUDF est dĂ©finie comme :
CREATE FUNCTION f(argument_1 INTEGER, argument_2 VARCHAR) ...
alors argumentInfo
contient des informations sur argument_1
et argument_2
.
argumentInfo
possÚde une propriété pour chacun de ces arguments. Chaque propriété est un objet avec les valeurs suivantes :
type
: chaĂźne. Le type de cet argument.isConst
: boolĂ©en. Si « true », la valeur de cet argument est constante (câest-Ă -dire quâelle est la mĂȘme pour chaque ligne).constValue
: siisConst
(comme dĂ©fini ci-dessus) est « true », cette entrĂ©e contient la valeur constante de lâargument ; sinon, ce champ estundefined
.
La fonction initialize()
ne peut pas produire de lignes de sortie.
Notes gĂ©nĂ©rales sur lâutilisation des fonctions de rappel¶
Les trois fonctions de rappel prennent un objet
context
; celui-ci est réservé pour une utilisation future et est actuellement vide.Prudence
La modification de lâobjet
context
peut entraĂźner un comportement indĂ©fini.Dâautres fonctions et propriĂ©tĂ©s peuvent ĂȘtre dĂ©finies, si nĂ©cessaire, sur lâobjet Ă utiliser dans lâUDTF.
Les arguments des fonctions de rappel sont positionnels et peuvent ĂȘtre nommĂ©s librement ; cependant, pour les besoins de ce chapitre, les noms ci-dessus sont utilisĂ©s pour le reste de la discussion et des exemples.
Partitions¶
Dans certaines situations, on peut vouloir regrouper les lignes en partitions. Le partitionnement présente deux avantages principaux :
Il vous permet de regrouper des lignes sur la base dâune caractĂ©ristique commune. Cela vous permet de traiter toutes les lignes du groupe ensemble, et de traiter chaque groupe indĂ©pendamment.
Il permet à Snowflake de diviser la charge de travail pour améliorer la parallélisation et donc les performances.
Par exemple, vous pouvez partitionner les donnĂ©es sur les cours des actions dans un groupe par action. Tous les prix des actions dâune entreprise individuelle peuvent ĂȘtre traitĂ©s ensemble, et les groupes de diffĂ©rentes entreprises sont traitĂ©s indĂ©pendamment.
Lâinstruction suivante appelle les UDTF nommĂ©es js_udtf()
sur des partitions individuelles. Chaque partition contient toutes les lignes pour lesquelles lâexpression PARTITION BY
donne la mĂȘme valeur (par exemple, le mĂȘme symbole boursier).
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expression>)) ...;
Lorsque vous spécifiez une expression de partition à utiliser avec une UDTF, Snowflake appelle :
initialize()
une fois pour chaque partition.processRow()
une fois pour chaque ligne individuelle de cette partition.finalize()
une fois pour chaque partition (aprÚs avoir traité la derniÚre ligne de cette partition).
Vous pouvez Ă©galement vouloir traiter les lignes de chaque partition dans un ordre prĂ©cis. Par exemple, si vous souhaitez calculer la moyenne mobile du cours dâune action dans le temps, vous devez ordonner les cours de lâaction par horodatage (ainsi que partitionner par action ou par sociĂ©tĂ©). Lâexemple suivant montre comment procĂ©der :
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expression> ORDER BY <expression>)) ...;
Lorsque vous spécifiez une clause ORDER BY
, les lignes sont traitĂ©es dans lâordre dĂ©fini par lâexpression ORDER BY
. Les lignes sont transmises dans processRow()
dans lâordre dĂ©fini par lâexpression ORDER BY
.
Dans la plupart des cas, le partitionnement des donnĂ©es amĂ©liore presque automatiquement les possibilitĂ©s de parallĂ©lisation et donc les performances. Snowflake exĂ©cute gĂ©nĂ©ralement plusieurs instances UDTF en parallĂšle. (Pour cette discussion, une instance dâune UDTF JavaScript est dĂ©finie comme une instance de lâobjet JavaScript utilisĂ©e pour reprĂ©senter la fonction dans Snowflake.) Chaque partition de lignes est transmise Ă une seule instance de lâUDTF.
Notez quâil nây a pas nĂ©cessairement de relation « une Ă une » entre les partitions et les instances dâUDTF. Bien que chaque partition ne soit traitĂ©e que par une seule instance dâUDTF, lâinverse nâest pas nĂ©cessairement vrai â une seule instance dâUDTF peut traiter plusieurs partitions. Il est donc important dâutiliser initialize()
et finalize()
pour configurer et dĂ©monter spĂ©cifiquement chaque partition, par exemple pour Ă©viter de « transfĂ©rer » les valeurs accumulĂ©es lors du traitement dâune partition vers le traitement dâune autre partition.
Colonnes de résultats¶
Lorsquâune table est jointe Ă une fonction de table, comme dans les exemples de partitionnement ci-dessus, le jeu de rĂ©sultats peut contenir les Ă©lĂ©ments suivants, selon ce qui est sĂ©lectionnĂ© :
Les colonnes définies dans la clause RETURNS de la commande CREATE FUNCTION.
Les colonnes de la table, y compris les colonnes utilisĂ©es pour partitionner les donnĂ©es et les autres colonnes, quâelles soient ou non utilisĂ©es comme paramĂštres dâentrĂ©e de lâUDTF.
Notez que les lignes produites dans le rappel processRow
et les lignes produites par finalize
diffĂšrent des maniĂšres suivantes :
Lorsquâune ligne est produite dans
processRow
, Snowflake peut la corrĂ©ler Ă une ligne en entrĂ©e, Ă savoir celle transmise dans la fonction en tant quâargumentrow
. Notez que si un appelprocessRow
donnĂ© produit plus dâune ligne, les attributs dâentrĂ©e sont corrĂ©lĂ©s dans chaque ligne de sortie.Pour les lignes produites en
processRow
, toutes les colonnes dâentrĂ©e peuvent ĂȘtre jointes Ă la relation de sortie.
Dans le rappel finalize
, Snowflake est incapable de le corrĂ©ler Ă une seule ligne, car il nâexiste pas de « ligne actuelle » Ă corrĂ©ler.
Pour les lignes produites dans le rappel
finalize
, seules les colonnes utilisĂ©es dans la clause PARTITION BY sont disponibles (car elles sont les mĂȘmes pour toutes les lignes de la partition courante) ; tous les autres attributs sont NULL. Si aucune clause PARTITION BY nâest spĂ©cifiĂ©e, tous ces attributs sont NULL.
Appeler des UDTFs JavaScript dans les requĂȘtes¶
Lorsque vous appelez une UDTF dans la clause FROM dâune requĂȘte, spĂ©cifiez le nom et les arguments de lâUDTF Ă lâintĂ©rieur des parenthĂšses qui suivent le mot-clĂ© TABLE.
En dâautres termes, utilisez une forme telle que la suivante pour le mot-clĂ© TABLE lorsque vous appelez une UDTF :
SELECT ...
FROM TABLE ( udtf_name (udtf_arguments) )
Note
Pour en savoir plus sur lâappel dâUDFs et dâUDTFs, voir ExĂ©cutez une UDF.
Pas de partitionnement¶
Cet exemple simple montre comment appeler un UDTF. Cet exemple transmet des valeurs littĂ©rales. LâUDTF renvoie simplement les paramĂštres dans lâordre inverse de leur ordre de transmission. Cet exemple nâutilise pas le partitionnement.
SELECT * FROM TABLE(js_udtf(10.0::FLOAT, 20.0::FLOAT));
+----+----+
| Y | X |
|----+----|
| 20 | 10 |
+----+----+
Cet exemple appelle un UDTF et lui transmet les valeurs dâune autre table. Dans cet exemple, lâUDTF nommĂ© js_udtf
est appelé une fois pour chaque ligne de la table nommée tab1
. Chaque fois que la fonction est appelée, les valeurs des colonnes c1
et c2
de la ligne en cours lui sont transmises. Comme ci-dessus, lâUDTF est appelĂ© sans clause PARTITION BY
.
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2)) ;
Lorsquâaucun partitionnement nâest utilisĂ©, le moteur dâexĂ©cution de Snowflake partitionne lâentrĂ©e elle-mĂȘme en fonction de diffĂ©rents facteurs, tels que la taille de lâentrepĂŽt virtuel traitant la fonction et la cardinalitĂ© de la relation dâentrĂ©e. Dans ce mode, le code utilisateur ne peut faire aucune supposition quant aux partitions. Ceci est particuliĂšrement utile lorsque la fonction nâa que besoin dâexaminer les lignes en isolation pour produire sa sortie, et aucun Ă©tat nâest agrĂ©gĂ© dâune ligne Ă lâautre.
Partitionnement explicite¶
Les UDTFs JavaScript peuvent aussi ĂȘtre appelĂ©s Ă lâaide dâune partition. Par exemple :
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY tab1.c3 ORDER BY tab1.c1));
Partitionnement explicite avec une clause OVER
vide¶
SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER ());
Une clause OVER
vide signifie que chaque ligne appartient Ă la mĂȘme partition (câest-Ă -dire que toute la relation dâentrĂ©e est une partition).
Note
Vous devez faire preuve de prudence lorsque vous appelez un UDTF JavaScript avec une clause OVER
vide, car cela limite Snowflake à créer une instance de la fonction et, par conséquent, Snowflake est incapable de paralléliser le calcul.
Exemples dâUDTFs JavaScript¶
Cette section contient plusieurs exemples dâUDTFs JavaScript.
Exemples Hello World
de base¶
LâUDTF JavaScript suivant ne prend aucun paramĂštre en compte et retourne toujours les mĂȘmes valeurs. Il est fourni principalement Ă titre dâillustration :
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());
Sortie :
+------------+
| OUTPUT_COL |
+============+
| Hello |
+------------+
| World |
+------------+
LâUDTF JavaScript suivant sert Ă©galement Ă des fins dâillustration, mais utilise un paramĂštre dâentrĂ©e. Notez que JavaScript est sensible Ă la casse, mais SQL force les identificateurs Ă ĂȘtre Ă©crits en majuscules. Par consĂ©quent, lorsque le code JavaScript fait rĂ©fĂ©rence Ă un nom de paramĂštre SQL, le code JavaScript doit utiliser des majuscules.
Notez également que les paramÚtres de fonction sont accessibles par le paramÚtre nommé row
dans la fonction get_params()
:
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'));
Sortie :
+------------+
| GREETING |
+============+
| Hello |
+------------+
| James |
+------------+
| Kirk |
+------------+
Exemples de base illustrant les fonctions de rappel¶
LâUDTF JavaScript suivant illustre toutes les fonctions de rappel dâAPI et les diffĂ©rentes colonnes de sortie. Il retourne simplement toutes les lignes telles quelles et fournit un dĂ©compte du nombre de caractĂšres vus dans chaque partition. Il illustre Ă©galement comment partager lâĂ©tat dâune partition en utilisant une rĂ©fĂ©rence THIS
. Notez que lâexemple utilise un rappel initialize()
pour rĂ©tablir le compteur Ă zĂ©ro ; cela est nĂ©cessaire, car une instance de fonction donnĂ©e peut ĂȘtre utilisĂ©e pour traiter plusieurs partitions :
-- 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;
}}';
La requĂȘte suivante illustre lâappel de lâUDTF CHAR_SUM
sur la table parts
sans partitionnement :
SELECT * FROM parts, TABLE(char_sum(s));
Sortie :
+--------+---------+-----+
| P | S | NUM |
+--------+---------+-----+
| 1 | michael | 7 |
| 1 | kelly | 5 |
| 1 | brian | 5 |
| 2 | clara | 5 |
| 2 | maggie | 6 |
| 2 | reagan | 6 |
| [NULL] | [NULL] | 34 |
+--------+---------+-----+
Lorsquâaucun partitionnement nâest spĂ©cifiĂ©, Snowflake dĂ©finit automatiquement les partitions. Dans cet exemple, en raison du petit nombre de lignes, une seule partition est créée (câest-Ă -dire quâun seul appel de finalize()
est exĂ©cutĂ©). Notez que la derniĂšre ligne contient des valeurs NULL dans les colonnes dâentrĂ©e.
MĂȘme requĂȘte, mais avec un partitionnement explicite :
SELECT * FROM parts, TABLE(char_sum(s) OVER (PARTITION BY p));
Sortie :
+--------+---------+-----+
| 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 |
+--------+---------+-----+
Cet exemple de partitions sur la colonne p
donne deux partitions. Pour chaque partition, une seule ligne est retournée dans le rappel finalize()
, ce qui donne un total de deux lignes, distinguées par la valeur NULL dans la colonne s
. Puisque p
est la colonne PARTITION BY, les lignes créées dans finalize()
ont la valeur p
qui définit la partition courante.
Exemples Ă©tendus utilisant les valeurs des tables et dâautres UDTFs comme entrĂ©es¶
Cet UDTF de base convertit une « plage » dâadresses IP en une liste complĂšte dâadresses IP. LâentrĂ©e se compose des 3 premiers segments de lâadresse IP (par exemple '192.168.1'
), puis du début et de la fin de la plage utilisée pour générer le dernier segment (par exemple 42
et 45
) :
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));
Sortie :
+--------------+
| IP_ADDRESS |
+==============+
| 192.168.1.42 |
+--------------+
| 192.168.1.43 |
+--------------+
| 192.168.1.44 |
+--------------+
| 192.168.1.45 |
+--------------+
En vous basant sur lâexemple prĂ©cĂ©dent, vous pouvez calculer des adresses IP individuelles pour plus dâune plage. Lâinstruction suivante crĂ©e une table de plages qui peut ĂȘtre utilisĂ©e pour sâĂ©tendre Ă des adresses IP individuelles. La requĂȘte entre ensuite les lignes de la table dans lâUDTF range_to_values()
pour retourner les adresses IP individuelles :
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;
Sortie :
+--------------+
| 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 |
+--------------+
Attention
Dans cet exemple, la syntaxe utilisĂ©e dans la clause FROM est identique Ă la syntaxe dâune jointure interne (câest-Ă -dire FROM t1, t2
) ; cependant, lâopĂ©ration effectuĂ©e nâest pas une jointure interne vraie. Le comportement rĂ©el est la fonction range_to_values()
qui est appelée avec les valeurs de chaque ligne de la table ip_address changes
. En dâautres termes, ce serait lâĂ©quivalent de lâĂ©criture :
for input_row in ip_address_ranges: output_row = range_to_values(input_row.prefix, input_row.range_start, input_row.range_end)
Le concept de transmettre des valeurs Ă un UDTF peut ĂȘtre Ă©tendu Ă plusieurs UDTFs. Lâexemple suivant crĂ©e un UDTF nommĂ© fake_ipv4_to_ipv6()
qui « convertit » une adresse IPV4 en adresses IPV6. La requĂȘte appelle alors la fonction dans le cadre dâune instruction plus complexe impliquant un autre UDTF :
-- 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'));
Sortie :
+-------------------------------+
| IPV6 |
+===============================+
| 192.168.3.100.000.000.000.000 |
+-------------------------------+
| 192.168.3.100.... |
+-------------------------------+
| 192.168.3.100.FFF.FFF.FFF.FFF |
+-------------------------------+
La requĂȘte suivante utilise les UDTFs fake_ipv4_to_ipv6
et range_to_values()
créés prĂ©cĂ©demment, avec lâentrĂ©e de la table ip_address changes
. En dâautres termes, elle commence par un ensemble de plages dâadresses IP, les convertit en adresses IPV4 individuelles, puis prend chaque adresse IPV4 et la « convertit » en une plage dâadresses IPV6 :
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
;
Sortie :
+------------------------------+
| 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 |
+------------------------------+
Notez que dans cet exemple, nous avons utilisĂ© deux fois la syntaxe de jointure, mais aucune des opĂ©rations nâĂ©tait une vraie jointure ; les deux Ă©taient des appels Ă un UDTF utilisant la sortie dâune table ou un autre UDTF comme entrĂ©e.
Une jointure intĂ©rieure vĂ©ritable est insensible Ă lâordre. Par exemple, les instructions suivantes sont identiques :
table1 INNER JOIN table2 ON ...
table2 INNER JOIN table1 ON ...
La saisie de valeurs dans un UDTF nâest pas une vĂ©ritable jointure, et les opĂ©rations ne sont pas insensibles Ă lâordre. Par exemple, la requĂȘte suivante est identique Ă lâexemple prĂ©cĂ©dent, sauf quâelle inverse lâordre des UDTFs dans la clause FROM :
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
;
La requĂȘte Ă©choue avec le message dâerreur suivant :
SQL compilation error: error line 3 at position 35 invalid identifier 'RTV.IP_ADDRESS'
Lâidentificateur rtv.ip_address
nâest pas valide, car il nâĂ©tait pas dĂ©fini avant son utilisation. Dans une vraie jointure, cela nâarriverait pas, mais lors du traitement des UDTFs en utilisant la syntaxe de jointure, cette erreur pourrait se produire.
Ensuite, essayez une instruction qui mĂ©lange lâentrĂ©e Ă un UDTF avec une jointure vraie ; cependant, rappelez-vous que lâentrĂ©e dâun UDTF et lâexĂ©cution dâune jointure interne utilisent la mĂȘme syntaxe, ce qui pourrait porter Ă confusion :
-- 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
;
Sortie :
+--------------+-------------------+
| 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 |
+--------------+-------------------+
Attention
Lâexemple prĂ©cĂ©dent fonctionne comme dĂ©crit ; cependant, vous devez faire attention lorsque vous combinez des UDTFs avec des jointures vraies, car cela pourrait entraĂźner un comportement non dĂ©terministe et/ou inattendu.
De plus, notez que ce comportement pourrait changer Ă lâavenir.