SparkSQL:我可以在同一查询中爆炸两个不同的变量吗?

问题描述 投票:8回答:4

我有以下爆炸查询,效果很好:

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")

但是我想知道是否可以一步完成?非常感谢!

apache-spark apache-spark-sql spark-dataframe
4个回答
21
投票

正确的语法是

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中返回任何受支持的类型,这意味着您可以一步完成更复杂的转换。


2
投票

改为尝试爆炸侧视图。

select name, color from data lateral view explode(names) as name lateral view explode(colors) as color;

0
投票

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")

您可以在多个数据帧中或在单个数据帧中进行上述选择,这对性能没有影响。


0
投票

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|
+------------+------------------+-----+-----+
© www.soinside.com 2019 - 2024. All rights reserved.