Apache spark SparkSQL:我可以在同一个查询中分解两个不同的变量吗?
我有以下explode查询,可以正常工作: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
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()
解释了一些问题