我在文件test.sql中有一个Spark SQL查询 -
CREATE GLOBAL TEMPORARY VIEW VIEW_1 AS select a,b from abc
CREATE GLOBAL TEMPORARY VIEW VIEW_2 AS select a,b from VIEW_1
select * from VIEW_2
现在,我启动我的spark-shell并尝试执行它 -
val sql = scala.io.Source.fromFile("test.sql").mkString
spark.sql(sql).show
此操作失败并出现以下错误 -
org.apache.spark.sql.catalyst.parser.ParseException:
mismatched input '<' expecting {<EOF>, 'GROUP', 'ORDER', 'HAVING', 'LIMIT', 'OR', 'AND', 'WINDOW', 'UNION', 'EXCEPT', 'MINUS', 'INTERSECT', 'SORT', 'CLUSTER', 'DISTRIBUTE'}(line 1, pos 128)
我尝试在不同的spark.sql语句中逐个执行这些查询,并且运行正常。问题是,我有6-7个查询创建临时视图,最后我需要从上一个视图输出。有没有办法在一个spark.sql语句中运行这些SQL。我曾经在Postgres SQL(Redshift)上工作,并且能够执行这种类型的查询。在spark sql中,在这种情况下我将不得不维护很多文件。
答案 0 :(得分:3)
问题是mkString
连接单个字符串中的所有行,无法正确解析为有效的SQL查询。
脚本文件中的每一行都应作为单独的查询执行,例如:
scala.io.Source.fromFile("test.sql").getLines()
.filterNot(_.isEmpty) // filter out empty lines
.foreach(query =>
spark.sql(query).show
)
如果查询分为多行,则情况稍微复杂一些。
我们绝对需要一个标记查询结束的令牌。让它成为分号字符,就像在标准SQL中一样。
首先,我们从源文件中收集所有非空行:
val lines = scala.io.Source.fromFile(sqlFile).getLines().filterNot(_.isEmpty)
然后我们处理收集的行,如果它不以分号结尾,则将每个新行与前一行连接起来:
val queries = lines.foldLeft(List[String]()) { case(queries, line) =>
queries match {
case Nil => List(line) // case for the very first line
case init :+ last =>
if (last.endsWith(";")) {
// if a query ended on a previous line, we simply append the new line to the list of queries
queries :+ line.trim
} else {
// the query is not terminated yet, concatenate the line with the previous one
val queryWithNextLine = last + " " + line.trim
init :+ queryWithNextLine
}
}
}