J'ai la requête d'éclatement suivante, qui fonctionne bien:
data1 = sqlContext.sql("select explode(names) as name from data")
Je veux faire exploser un autre champ "couleurs", donc la sortie finale pourrait être le produit cartésien des noms et des couleurs. J'ai donc fait:
data1 = sqlContext.sql("select explode(names) as name, explode(colors) as color from data")
Mais j'ai eu les erreurs:
Only one generator allowed per select but Generate and and Explode found.;
Est-ce que quelqu'un a une idée?
Je peux réellement le faire fonctionner en faisant deux étapes:
data1 = sqlContext.sql("select explode(names) as name from data")
data1.registerTempTable('data1')
data1 = sqlContext.sql("select explode(colors) as color from data1")
Mais je me demande s'il est possible de le faire en une seule étape? Merci beaucoup!
La syntaxe correcte est
select name, color
from data
lateral view explode(names) exploded_names as name
lateral view explode(colors) exploded_colors as color
La raison pour laquelle la réponse de Rashid n'a pas fonctionné est qu'elle n'a pas "nommé" la table générée par LATERAL VIEW
.
Pensez-y de cette façon: LATERAL VIEW
Fonctionne comme un JOIN
implicite avec une table éphémère créée pour chaque ligne à partir du structs
dans la collection en cours de "visualisation". Ainsi, la façon d'analyser la syntaxe est la suivante:
LATERAL VIEW table_generation_function(collection_column) table_name AS col1, ...
Si vous utilisez une fonction de génération de table telle que posexplode()
, vous avez toujours une table de sortie mais avec plusieurs colonnes de sortie:
LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
Vous pouvez également "imbriquer" LATERAL VIEW
En éclatant à plusieurs reprises des collections imbriquées, par exemple,
LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
LATERAL VIEW posexplode(order.items) exploded_items AS item_number, item
Bien que nous soyons sur le sujet de LATERAL VIEW
, Il est important de noter que son utilisation via SparkSQL est plus efficace que son utilisation via le DataFrame
DSL, par exemple, myDF.explode()
. La raison en est que SQL peut raisonner avec précision sur le schéma tandis que l'API DSL doit effectuer une conversion de type entre un type de langage et la ligne de trame de données. Ce que l'API DSL perd en termes de performances, cependant, il gagne en flexibilité car vous pouvez renvoyer n'importe quel type pris en charge à partir de explode
, ce qui signifie que vous pouvez effectuer une transformation plus compliquée en une seule étape.
Dans les versions récentes de Spark, l'explosion au niveau ligne via df.explode()
a été déconseillée au profit d'une explosion au niveau colonne via df.select(..., explode(...).as(...))
. Il existe également une explode_outer()
, qui produira des lignes de sortie même si l'entrée à exploser est null
. L'explosion au niveau de la colonne ne souffre pas des problèmes de performances de l'explosion au niveau de la ligne mentionnés ci-dessus car Spark peut effectuer la transformation entièrement en utilisant des représentations de données de ligne internes.
Essayez plutôt une vue latérale exploser.
select name, color from data lateral view explode(names) as name lateral view explode(colors) as color;
Plus d'une explosion n'est pas autorisée dans spark sql car c'est trop déroutant. C'est parce que vous obtenez un produit cartésien implicite des deux choses que vous explosez. Si vous voulez en faire plus d'une exploser, vous devez utiliser plusieurs sélections. Hive a une vue latérale qui peut atteindre ce dont vous avez besoin (expliqué par Rashid ALi dans sa réponse ici). Je recommanderais personnellement deux sélections avec des trames de données car il est beaucoup plus efficace en étincelle. Supposons maintenant que "données" est un bloc de données.
val data1 = data.select($"id",$"names",$explode($"colors").alias("colors"))
//select required columns from colors
.select($"id",$"colors.field1",explode($"names").alias("names"))
//now select required cols from names
.select($"id",$"field1",$"names.col1",$"names.col2")
Vous pouvez faire des sélections ci-dessus dans plusieurs trames de données ou dans une seule comme ci-dessus, cela ne fait aucune différence en termes de performances.
Il existe un moyen simple d'exploser sur plusieurs colonnes par df.withColumn
.
scala> val data = spark.sparkContext.parallelize(Seq((Array("Alice", "Bob"), Array("Red", "Green", "Blue"))))
.toDF("names", "colors")
data: org.Apache.spark.sql.DataFrame = [names: array<string>, colors: array<string>]
scala> data.show
+------------+------------------+
| names| colors|
+------------+------------------+
|[Alice, Bob]|[Red, Green, Blue]|
+------------+------------------+
scala> data.withColumn("name", explode('names))
.withColumn("color", explode('colors))
.show
+------------+------------------+-----+-----+
| names| colors| name|color|
+------------+------------------+-----+-----+
|[Alice, Bob]|[Red, Green, Blue]|Alice| Red|
|[Alice, Bob]|[Red, Green, Blue]|Alice|Green|
|[Alice, Bob]|[Red, Green, Blue]|Alice| Blue|
|[Alice, Bob]|[Red, Green, Blue]| Bob| Red|
|[Alice, Bob]|[Red, Green, Blue]| Bob|Green|
|[Alice, Bob]|[Red, Green, Blue]| Bob| Blue|
+------------+------------------+-----+-----+