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

Apache spark SparkSQL:我可以在同一个查询中分解两个不同的变量吗?,apache-spark,apache-spark-sql,spark-dataframe,Apache Spark,Apache Spark Sql,Spark Dataframe,我有以下explode查询,可以正常工作: data1 = sqlContext.sql("select explode(names) as name from data") 我想分解另一个字段“colors”,这样最终的输出可以是名称和颜色的笛卡尔乘积。所以我做了: data1 = sqlContext.sql("select explode(names) as name, explode(colors) as color from data") 但我犯了错误: Only one gen

我有以下explode查询,可以正常工作:

data1 = sqlContext.sql("select explode(names) as name from data")
我想分解另一个字段“colors”,这样最终的输出可以是名称和颜色的笛卡尔乘积。所以我做了:

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) as name lateral view explode(colors) as color;

spark sql中不允许有多个爆炸,因为它太容易混淆。这是因为你得到了两件事情的隐式笛卡尔积。如果要进行多个爆炸,则必须使用多个 不止一个选择。蜂巢有一个侧面视图,可以满足您的需求(拉希德·阿里在这里的回答中解释)。我个人推荐两款带有数据帧的select,因为它在spark中非常有效。现在假设“数据”是一个数据帧

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")
您可以在多个数据帧或像上面这样的单个数据帧中执行上述选择,这对性能没有影响

正确的语法是

select name, color 
from data 
lateral view explode(names) exploded_names as name 
lateral view explode(colors) exploded_colors as color
拉希德的答案不起作用的原因是它没有“命名”由
横向视图生成的表

解释 可以这样想:
横向视图
的工作原理类似于隐式的
连接
,为“查看”集合中的
结构
中的每一行创建一个临时表。因此,解析语法的方法是:

LATERAL VIEW table_generation_function(collection_column) table_name AS col1, ...
多个输出列 如果使用表生成函数,如
posexplode()
,则仍有一个输出表,但有多个输出列:

LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
筑巢 您还可以通过重复分解嵌套集合来“嵌套”
横向视图
,例如

LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
LATERAL VIEW posexplode(order.items) exploded_items AS item_number, item
性能注意事项 当我们讨论
横向视图
时,需要注意的是,通过SparkSQL使用它比通过
数据帧
DSL使用它更有效,例如,
myDF.explode()
。原因是SQL可以准确地推理模式,而DSLAPI必须在语言类型和数据帧行之间执行类型转换。然而,DSL API在性能方面失去的是灵活性,因为您可以从
explode
返回任何支持的类型,这意味着您可以在一个步骤中执行更复杂的转换

更新
在Spark的最新版本中,行级分解通过
df.explode()
已被弃用,取而代之的是列级分解通过
df.select(…,explode(…).as(…)
。还有一个
explode\u outer()
,即使要分解的输入为
null
,也会生成输出行。列级分解不会遇到上述行级分解的性能问题,因为Spark可以完全使用内部行数据表示来执行转换。

有一种简单的方法可以通过
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|
+------------+------------------+-----+-----+
scala>val data=spark.sparkContext.parallelize(Seq((数组(“Alice”、“Bob”)、数组(“红色”、“绿色”、“蓝色”))
.toDF(“名称”、“颜色”)
data:org.apache.spark.sql.DataFrame=[名称:数组,颜色:数组]
scala>data.show
+------------+------------------+                                               
|名称|颜色|
+------------+------------------+
|[爱丽丝,鲍勃]|[红,绿,蓝]|
+------------+------------------+
scala>data.withColumn(“名称”,分解(“名称))
.withColumn(“颜色”,explode(“颜色))
显示
+------------+------------------+-----+-----+
|名称|颜色|名称|颜色|
+------------+------------------+-----+-----+
|[爱丽丝,鲍勃]|[红,绿,蓝]|爱丽丝|红|
|[爱丽丝,鲍勃]|[红,绿,蓝]|爱丽丝|绿|
|[爱丽丝,鲍勃]|[红,绿,蓝]|爱丽丝|蓝|
|[爱丽丝,鲍勃]|[红,绿,蓝]|鲍勃|红|
|[爱丽丝,鲍勃]|[红,绿,蓝]|鲍勃|绿|
|[爱丽丝,鲍勃]|[红,绿,蓝]|鲍勃|蓝|
+------------+------------------+-----+-----+

选择名称,从数据横向视图中选择颜色分解(名称)作为名称横向视图分解(颜色)作为颜色;^SyntaxError:无效的syntax可能会弄乱语法,请查看此页了解正确的语法谢谢!我想知道这里的$“id”是什么?这里的$“names.col1”和$“names.col2”id是什么?@Edamame$“id”是我添加的一个示例列$“names.col1”和$“names.col2”是名称Json字段中的嵌套列。因为你使用的是爆炸。我假设您的示例数据与此名称类似,我刚刚尝试过,但出现以下错误:data1=data.select($“id”),$“names”,$explode($“colors”).alias(“colors”)^syntaxer错误:可能是因为我使用的是python而不是scala,所以语法无效?谢谢@Edamame不,不是真的,scala和python在数据帧方面没有那么大的不同。在您发布的示例代码中,在“选择您正在使用字段“id”中,您的数据中是否有字段名“id”?如果没有,请将其删除。并用您的源数据样本更新帖子,这将有助于我根据数据编写代码。@RameshMaharjan多个生成器不仅在单个
select()
中受支持。这是因为DSL通过
explode()
横向连接隐藏为投影。因为
explode()
不是一个投影,所以这是一种编码便利,也是一个混乱的来源。为了避免混淆,许多人更喜欢使用列(“…”、分解(…)
,您可以根据需要链接任意多个列,每个列都成为物理计划中的生成器。这就是我所说的
explode()
“返回”数据帧的意思。我指的是爆炸的结果,而不是函数本身(DSL中所有函数都返回
),谢谢!你关于
横向视图
更有效的评论。explode()
解释了一些问题