scala - 如何获取(派生的)Spark DataFrame 的查询逻辑的 SQL 表示?
问题描述
可以将原始 SQL 字符串转换为 DataFrame。但是否也有可能反过来,即获取(派生的)Spark DataFrame 的查询逻辑的 SQL 表示?
// Source data
val a = Seq(7, 8, 9, 7, 8, 7).toDF("foo")
// Query using DataFrame functions
val b = a.groupBy($"foo").agg(count("*") as "occurrences").orderBy($"occurrences")
b.show()
// Convert a SQL string into a DataFrame
val sqlString = "SELECT foo, count(*) as occurrences FROM a GROUP BY foo ORDER BY occurrences"
a.createOrReplaceTempView("a")
val c = currentSparkSession.sql(sqlString)
c.show()
// "Convert" a DataFrame into a SQL string
b.toSQLString() // Error: This function does not exist.
解决方案
不可能将 DataFrame “转换”为 SQL 字符串,因为 Spark 不知道如何编写 SQL 查询,它也不需要。
我发现回忆一下 Spark 如何处理 Dataframe 代码或 SQL 查询很有用。这是由 Spark 的Catalyst Optimizer完成的,它经历了四个转型阶段,如下所示:
在第一阶段(分析)中,Spark SQL 引擎为 SQL 或 Dataframe 查询生成抽象语法树 ( AST )。该树是 Catalyst 中的主要数据类型(请参阅白皮书Spark SQL:Spark 中的关系数据处理中的第 4.1 节),它用于创建逻辑计划并最终创建物理计划。如果您使用explain
Spark 提供的 API,您将获得这些计划的表示。
虽然我很清楚“一个人可以将原始 SQL 字符串转换为 DataFrame”是什么意思,但我想这有助于更精确。我们不会将SQL 字符串(因此您自己在该词周围加上引号)转换为 Dataframe,但您应用了您的 SQL 知识,因为这是一种可以被Spark解析以理解您的意图的语法。此外,您不能只键入任何 SQL 查询,因为在与目录进行比较时,这仍然可能在分析阶段失败。因此,SQL 字符串只是关于 Spark 允许您如何给出指令的协议。然后,这个 SQL 查询被解析,转换成一个 AST(如上所述),并在经历了其他三个阶段之后,最终形成一个基于 RDD 的代码。此 SQL 执行的结果通过sql
API 返回一个 Dataframe,而您可以使用df.rdd
.
总的来说,Spark 不需要将任何代码,特别是任何 Dataframe 代码编写成 SQL 语法,然后您就可以从 Spark 中脱离出来。AST 是内部抽象,Spark 不需要先将 Dataframe 代码转换为 SQL 查询,而是直接将 Dataframe 代码转换为 AST。