Spark SQL 解析之后是 Unresolved Logical Plan,经过 Analyzer 之后变为 Resolved Logical Plan。
用的规则组成一个 batches: Seq[Batch]。一个 Batch 包含一组规则和这组规则的执行次数限制。
with 表达式替换。
匹配到 WithWindowDefinition 表达式时,将其子节点中未解析的窗口函数表达式(UnsolvedWindowExpression)转换成窗口函数表达式。
当 union 算子节点只有一个子节点时,Union 操作实际上并没有起到作用,这种情况下应该消除该 Union 节点。
如以下 SQL 生成的执行计划去掉了 union。
spark-sql> explain select * from t1
union all
select * from t1 where 1=2;
plan
== Physical Plan ==
Scan hive test.t1 [c1#19], HiveTableRelation [`test`.`t1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [c1#19], Partition Cols: []]
Time taken: 0.459 seconds, Fetched 1 row(s)
Order by
和 Group by
语句可以用常熟表示下标。分别用参数spark.sql.orderByOrdinal
和 spark.sql.groupByOrdinal
控制,默认为 true
。
如以下示例中,HashAggregate 的 keys 变为了 c1,而不是 1。
spark-sql> explain select c1 from t1 group by 1;
plan
== Physical Plan ==
HashAggregate(keys=[c1#51], functions=[])
+- Exchange hashpartitioning(c1#51, 1), ENSURE_REQUIREMENTS, [plan_id=104]
+- HashAggregate(keys=[c1#51], functions=[])
+- Scan hive test.t1 [c1#51], HiveTableRelation [`test`.`t1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [c1#51], Partition Cols: []]
Time taken: 0.161 seconds, Fetched 1 row(s)
包含 ResolveNamespace(catalogManager)
, ResolveRelations
, ResolveTables
等25条规则。
仅包含一个规则 PullOutNondeterministic。将非 Project 或非 Filter 算子中的 nondeterministic 表达式提取出来,然后将这些表达式放在内层的 Project 算子中或最终的 Project 算子中。
对用户自定义函数做特除处理。如果自定义函数的参数有基本数据类型,生成 if 表达式。
示例
spark-sql> desc test.tab1;
col_name data_type comment
a1 int
a2 int
Time taken: 0.322 seconds, Fetched 2 row(s)
spark-shell --master local[2] 进入 spark-shell
val doublePara = (i:Int) => 2 * i
val doubleUDF = udf(doublePara)
spark.udf.register("doubleUDF", doubleUDF)
spark.sql("explain select doubleUDF(a1) from test.tab1").collect().foreach(println)
输出结果如下,可以看到,由于 a1 列的值可能为 null,则生成表达式为if (isnull(a1#20)) null else doubleUDF(knownnotnull(a1#20)) AS doubleUDF(a1)#22
:
[== Physical Plan ==
Project [if (isnull(a1#20)) null else doubleUDF(knownnotnull(a1#20)) AS doubleUDF(a1)#22]
+- Scan hive test.tab1 [a1#20], HiveTableRelation [`test`.`tab1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [a1#20, a2#21], Partition Cols: []]
]
在 DataFrame 或 DataSet 等编程接口中,用户代码对于某些列的操作可能改变其 nullability 属性。本规则更新属性的 nullability,如以下示例,b.id 是非空的,但是a join b 之后,b.id 字段变为可能为 null。
参考 SPARK-13484, SPARK-13801
val a = spark.range(10).select(col("id"), lit(0).as("count"))
val b = spark.range(10).select((col("id") % 3).as("id")).groupBy("id").count()
a.join(b, a("id") === b("id"), "left_outer").filter(b("count").isNull).show()
清理无用的别名信息。仅仅在最上级的 project 列表或者汇聚或者 windows 函数的别名不能去掉,其他的都可以去掉。
以下的查询中,子查询把 c1 别名为 c2,外部把 c2 别名为 c3。优化后,去掉 c2,外部把 c1 别名为 c3。
spark-sql> explain select c2 c3 from (select c1 c2 from t1)t;
plan
== Physical Plan ==
Project [c1#42 AS c3#36]
+- Scan hive test.t1 [c1#42], HiveTableRelation [`test`.`t1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [c1#42], Partition Cols: []]
Time taken: 0.316 seconds, Fetched 1 row(s)