Une fonction définie par l'utilisateur (UDF) est un moyen pour un utilisateur d'étendre les capacités natives d'Apache Spark™. Le SQL sur Databricks prend en charge les UDF externes écrites en Scala, Java, Python et R depuis la version 1.3.0. Bien que les UDF externes soient très puissantes, elles présentent quelques inconvénients :
Pour faire face aux limitations ci-dessus, nous sommes ravis de présenter une nouvelle forme d'UDF : les UDF SQL. Disponible dans DBR 9.1 LTS, l'UDF SQL est entièrement définie avec la puissance expressive de SQL et est également complètement transparente pour le compilateur SQL.
Consultez le livre blanc Pourquoi le Data Lakehouse est votre prochain Data Warehouse pour découvrir le fonctionnement interne de la plateforme Databricks Lakehouse.
Les UDF SQL sont des extensions simples mais puissantes du SQL sur Databricks. En tant que fonctions, elles fournissent une couche d'abstraction pour simplifier la construction des requêtes, rendant les requêtes SQL plus lisibles et modulaires. Contrairement aux UDF écrites dans un langage non SQL, les UDF SQL sont plus légères à créer pour les utilisateurs SQL. Les corps des fonctions SQL sont transparents pour l'optimiseur de requêtes, ce qui les rend plus performantes que les UDF externes. Les UDF SQL peuvent être créées comme des fonctions temporaires ou permanentes, être réutilisées dans plusieurs requêtes, sessions et utilisateurs, et être contrôlées d'accès via le langage de contrôle d'accès (ACL). Dans ce blog, nous allons vous présenter quelques cas d'utilisation clés des UDF SQL avec des exemples.
Commençons par la fonction la plus simple imaginable : une constante. Nous savons tous que nous ne sommes pas censés utiliser de littéraux dans notre code car cela nuit à la lisibilité et, qui sait, peut-être que la constante ne reste pas constante après tout. Nous voulons donc pouvoir la modifier en un seul endroit :
Si vous êtes familier avec les UDF externes, vous pouvez constater qu'il y a quelques différences notables :
Au-delà de ces différences, de nombreuses autres choses sont identiques aux UDF externes :
Utilisons la fonction :
Sans surprise, cela fonctionne. Mais que se passe-t-il sous le capot ?
C'est génial ! Le compilateur SQL a remplacé l'appel de fonction par la constante elle-même.
Cela signifie qu'au moins cette UDF SQL n'a aucun coût en termes de performance.
Maintenant, examinons un autre modèle d'utilisation courant.
Imaginez que vous n'aimez pas la dénomination de certaines fonctions intégrées. Peut-être migrez-vous de nombreuses requêtes depuis un autre produit, qui a des noms et des comportements de fonctions différents. Ou peut-être ne supportez-vous pas de copier-coller des expressions longues encore et encore dans vos requêtes SQL. Vous voulez donc y remédier.
Avec les UDF SQL, nous pouvons simplement créer une nouvelle fonction avec le nom que nous aimons :
Examinons la nouvelle syntaxe utilisée ici :
Non seulement cela fonctionne...
... mais cela fonctionne bien :
Nous pouvons voir que le plan physique montre une application directe des fonctions lpad, hex, least et greatest. C'est le même plan que celui obtenu en appelant directement la série de fonctions.
Vous pouvez également composer des fonctions SQL à partir d'autres fonctions SQL :
Une autre utilisation courante des UDF SQL est de codifier des recherches. Une recherche simple peut consister à décoder des codes de couleur RVB en noms de couleur anglais :
OK, mais il y a beaucoup plus que deux couleurs dans ce monde. Et nous voulons cette traduction dans les deux sens, donc cela devrait vraiment être dans une table de correspondance :
Plusieurs nouveaux concepts sont appliqués ici :
À quoi ressemble le plan physique maintenant ? Il est facile de constater qu'utiliser une UDF externe, qui effectue elle-même une requête résultant en une jointure en boucle imbriquée, est une façon horrible de gaspiller de précieuses ressources.
Dans ce cas, Catalyst a choisi un jointure par hachage diffusée (broadcast hash join) au lieu d'une jointure en boucle imbriquée (nested loop join). Il peut le faire car il comprend le contenu de l'UDF SQL.
Jusqu'à présent, tous les exemples abordés utilisaient des fonctions à valeur scalaire – celles qui retournent une seule valeur. Ce résultat peut être de n'importe quel type, même des combinaisons complexes de structures, de tableaux et de cartes. Il existe un autre type d'UDF à aborder – l'UDF à valeur de table.
Imaginez si les vues prenaient des arguments ! Vous pourriez encapsuler des prédicats complexes même s'ils dépendent de valeurs fournies par l'utilisateur. Une UDF de table SQL est exactement cela : une vue par un autre nom, sauf qu'elle a des paramètres.
Supposons que le mappage de couleurs ci-dessus ne soit pas unique. Au minimum, nous pouvons affirmer que les noms de couleurs diffèrent selon les langues.
Par conséquent, la fonction `from_rgb` doit être modifiée pour retourner soit un tableau de noms, soit une relation.
Comme vous pouvez le voir, la seule différence par rapport à une fonction scalaire est une clause RETURNS plus complexe. Contrairement aux vues, les UDF SQL exigent une déclaration de la signature de la relation retournée :
Les fonctions de table définies par l'utilisateur sont nouvelles dans DBR. Jetons un coup d'œil à la façon de les invoquer.
Dans sa forme la plus simple, une fonction de table est invoquée de la même manière et aux mêmes endroits qu'une vue est référencée. La seule différence réside dans les parenthèses obligatoires, qui incluent les arguments de la fonction. Cette fonction est invoquée avec des arguments littéraux, mais les arguments peuvent être n'importe quelle expression, même des sous-requêtes scalaires.
Le plus puissant, cependant, est l'utilisation d'une UDF de table SQL dans une jointure, typiquement une jointure croisée corrélée :
Ici, les arguments font référence (corrèlent) à une relation précédente (latérale) dans la clause FROM. Le nouveau mot-clé LATERAL donne à Catalyst la permission de résoudre ces colonnes. Notez également que vous pouvez faire référence au résultat de la fonction de table en nommant les colonnes telles que définies dans la signature du résultat et éventuellement qualifiées par le nom de la fonction.
Naturellement, les UDF SQL sont entièrement prises en charge par les instructions GRANT, REVOKE, SHOW, DESCRIBE et DROP existantes.
L'instruction qui mérite d'être soulignée plus en détail est DESCRIBE.
La description de base renvoie ce à quoi vous pourriez vous attendre, mais la commande DESCRIBE étendue ajoute beaucoup plus de détails :
Ce que nous avons décrit représente la fonctionnalité initiale pour les UDF SQL. Les futures extensions que nous envisageons incluent la prise en charge de :
Les UDF SQL représentent une grande avancée en matière d'utilisabilité de SQL et peuvent être utilisées de nombreuses manières différentes, comme décrit dans ce blog. Nous vous encourageons à penser à des moyens encore plus créatifs d'exploiter les UDF SQL, que ce soit dans Databricks SQL ou en utilisant Photon pour les tâches d'ingénierie de données. Essayez le notebook ici et consultez la documentation pour plus d'informations.
(Cet article de blog a été traduit à l'aide d'outils basés sur l'intelligence artificielle) Article original
