我有以下爆炸查询,效果很好:
data1 = sqlContext.sql("select explode(names) as name from data")
我想爆炸另一个字段“颜色”,因此最终输出可能是名称和颜色的笛卡尔积。所以我做了:
data1 = sqlContext.sql("select explode(names) as name, explode(colors) as color from data")
但是我得到了错误:
Only one generator allowed per select but Generate and and Explode found.;
有人有什么主意吗?
我实际上可以通过执行两个步骤来使其工作:
data1 = sqlContext.sql("select explode(names) as name from data")
data1.registerTempTable('data1')
data1 = sqlContext.sql("select explode(colors) as color from data1")
但是我想知道是否可以一步完成?非常感谢!
正确的语法是
select name, color
from data
lateral view explode(names) exploded_names as name
lateral view explode(colors) exploded_colors as color
Rashid的答案不起作用的原因是,它没有“命名” LATERAL VIEW
生成的表。
[这样考虑:LATERAL VIEW
的工作方式类似于隐式JOIN
,其中为正在“查看”的集合中structs
中的每一行创建了一个临时表。因此,解析语法的方法是:
LATERAL VIEW table_generation_function(collection_column) table_name AS col1, ...
如果使用表生成功能,例如posexplode()
,则仍然有一个输出表,但具有多个输出列:
LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
您还可以通过重复展开嵌套集合,例如“嵌套” LATERAL VIEW
LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
LATERAL VIEW posexplode(order.items) exploded_items AS item_number, item
虽然我们是LATERAL VIEW
的主题,但必须注意,通过SparkSQL使用它比通过DataFrame
DSL(例如myDF.explode()
)使用它更有效。原因是当DSL API必须在语言类型和数据帧行之间执行类型转换时,SQL可以对模式进行准确的推理。 DSL API在性能方面失去了什么,但是,它具有灵活性,因为您可以从explode
中返回任何受支持的类型,这意味着您可以一步完成更复杂的转换。
改为尝试爆炸侧视图。
select name, color from data lateral view explode(names) as name lateral view explode(colors) as color;
Spark sql中不允许发生多个爆炸,因为这太令人困惑了。这是因为您得到了爆炸的两件事的隐式笛卡尔积。如果要爆炸不止一次,则必须使用更多爆炸比一个选择。 Hive具有可以满足您需要的侧面视图(Rashid Ali在此处的回答中对此进行了解释)。我个人建议使用带有数据帧的两个选择,因为它在火花处理方面非常有效。现在假设“数据”是一个数据帧。
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")
您可以在多个数据帧中或在单个数据帧中进行上述选择,这对性能没有影响。
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|
+------------+------------------+-----+-----+