Sparksql Expression的canonicalized(规范化)操作

​啥是Expression?

在sql语句中,除了select、from等关键字以外,其他大部分元素都可以理解为expression,比如:

select a,b from testdata2 where a>2 

这里的 a,b,>,2都是expression

Expression的canonicalized操作

这个操作返回经过规范化处理后的表达式

规范化处理会在确保输出结果相同的前提下通过一些规则对表达式进重写

这个规范化有啥用呢?

举个例子:

select b,B,sum(A+b) as ab,sum(B+a) as ba from testdata2 
where  b>3 group by b

上面的代码中,b和B,sum(A+b)和sum(B+a) 虽然在外观上不一样,但实际计算的结果是完全一样的。而规范化操作会把b,B 和 sum(A+b)和sum(B+a)在外观上统一,这样可以使它们引用同一个实际计算的结果,避免多次计算。

这个规范化具体是怎么操作的呢?

借助了工具类:Canonicalize

核心方法execute

依据一些规则重写表达式,消除外观差异

def execute(e: Expression): Expression = {
       expressionReorder(ignoreNamesTypes(e))
  }

规范化结果集中的命名

两种情况:

  • 对于AttributeReference引用类的表达式,主要做法是消除名称和可空性带来的差异

  • GetStructField复杂类型的表达式,消除名称带来的差异

对于引用类型的表达式,判断是否相同,只需要引用的id(exprId)是相同的就ok,所以这里的处理方法是把name统一置none,来消除差异,比如:

select b,B,sum(A+b) as ab,sum(B+a) as ba from testdata2 where b>3 group by b
​
//name#exprId
Expression(b) ---> b#3   
ignoreNamesTypes(b)----none#3
​
Expression(B) ---> B#3
ignoreNamesTypes(B)----none#3
​
结过上面转化,b和B都为none#3

计算类表达式

  • 对于交换和结合运算(Add和Multiply)的子运算,通过“hashCode”来对左右子节点排序

  • 对于交换和结合运算(Or和And)的子运算,通过“hashCode”来对左右子节点排序,但要求表达式必须是确定性的

  • EqualTo和EqualNullSafe通过“hashCode”来对左右子节点排序。

  • 其他比较(greatethan,LessThan)由“hashCode”反转。

  • in中的元素按`hashCode'重新排序

private def expressionReorder(e: Expression): Expression = e match {
    // 加法和乘法可以交换顺序,转换为seq,用Seq的sortBy进行排序
    case a: Add => orderCommutative(a, { case Add(l, r) => Seq(l, r) }).reduce(Add)
    case m: Multiply => orderCommutative(m, { case Multiply(l, r) => Seq(l, r) }).reduce(Multiply)
   // or和and可以交换顺序,转换为seq,用Seq的sortBy进行排序,但要求必须是确定性的
    case o: Or =>
      orderCommutative(o, { case Or(l, r) if l.deterministic && r.deterministic => Seq(l, r) })
        .reduce(Or)
    case a: And =>
      orderCommutative(a, { case And(l, r) if l.deterministic && r.deterministic => Seq(l, r)})
        .reduce(And)
    // EqualTo和EqualNullSafe通过“hashCode”来对左右子节点排序
    case EqualTo(l, r) if l.hashCode() > r.hashCode() => EqualTo(r, l)
    case EqualNullSafe(l, r) if l.hashCode() > r.hashCode() => EqualNullSafe(r, l)
​
    // 其他比较greatethan,LessThan由“hashCode”反转,比如 在>时,如果l.hashCode() > r.hashCode(),就转为 LessThan(r, l)
    case GreaterThan(l, r) if l.hashCode() > r.hashCode() => LessThan(r, l)
    case LessThan(l, r) if l.hashCode() > r.hashCode() => GreaterThan(r, l)
​
    case GreaterThanOrEqual(l, r) if l.hashCode() > r.hashCode() => LessThanOrEqual(r, l)
    case LessThanOrEqual(l, r) if l.hashCode() > r.hashCode() => GreaterThanOrEqual(r, l)
​
    // Note in the following `NOT` cases, `l.hashCode() <= r.hashCode()` holds. The reason is that
    // canonicalization is conducted bottom-up -- see [[Expression.canonicalized]].
    case Not(GreaterThan(l, r)) => LessThanOrEqual(l, r)
    case Not(LessThan(l, r)) => GreaterThanOrEqual(l, r)
    case Not(GreaterThanOrEqual(l, r)) => LessThan(l, r)
    case Not(LessThanOrEqual(l, r)) => GreaterThan(l, r)
​
    // in按`hashCode'重新排序
    case In(value, list) if list.length > 1 => In(value, list.sortBy(_.hashCode()))
​
    case _ => e
  }
}

 扩展操作semanticEquals

// 两个表达式计算相同的结果时返回true,判断依据是:两个表达式都确定性的,
  // 且两个表达式规范化之后相同
  def semanticEquals(other: Expression): Boolean =
    deterministic && other.deterministic && canonicalized == other.canonicalized

​Hey!

我是小萝卜算子

 每天学习一点点

​知识增加一点点

思考深入一点点

在成为最厉害最厉害最厉害的道路上

很高兴认识你

推荐阅读--

数仓面试——日期交叉问题

数仓面试——连续登录问题进阶版

数仓面试——连续登录问题

转型【数仓开发】该怎么学

大数据开发轻量级入门方案

OLAP | 基础知识梳理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小萝卜算子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值