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

时间:2016-04-26 22:30:54

标签: apache-spark apache-spark-sql spark-dataframe

我有以下爆炸查询,工作正常:

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

但我想知道是否有可能一步到位?非常感谢!

4 个答案:

答案 0 :(得分:20)

正确的语法是

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()。原因是SQL可以准确地推断出模式,而DSL API必须在语言类型和数据帧行之间执行类型转换。但是,在性能方面,DSL API会失败,因为您可以从explode返回任何支持的类型,从而获得灵活性,这意味着您可以在一个步骤中执行更复杂的转换。

答案 1 :(得分:2)

尝试侧视图爆炸。

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

答案 2 :(得分:0)

spark sql中不允许多次爆炸,因为它太混乱了。这是因为你得到了你正在爆炸的两件事的隐含笛卡尔积。如果你想做多个爆炸,你必须使用更多 比一个选择。 Hive有一个侧视图,可以达到你所需要的(Rashid Ali在他的回答中解释)。我个人会推荐两个带有数据帧的选择,因为它在spark方面效率很高。现在假设'data'是一个数据框。

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

你可以在多个数据帧中或在上面的单个数据帧中进行上述选择,但它对性能没有任何影响。

答案 3 :(得分: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|
+------------+------------------+-----+-----+