查询优化中的物化视图改写(二):SPOJG的改写

539 阅读4分钟

上一篇文章中我们描述了SPJG的物化视图的改写,其中的J只能是inner join。本文中我们将描述outer join的物化视图的改写。与inner join不同,我们不能将其简单地视为笛卡尔积的问题,而是需要一些复杂的变换。

定义

这里我们对后面要用到的符号和定义做一些总结。

nullrejectingnull-rejecting: 当一个selection条件pp所引用的任意列是nullnull,则pp肯定不为truetrue,这是我们说ppnullrejectingnull-rejecting

null(T)null(T): 当某一行在表TT的列上全是nullnull,则null(T)null(T)为真。比如T1T2T_1 \rtimes T_2的结果中,当T1T_1中的某行t1t_1T2T_2中没有找到任何匹配的行时,我们需要对T2T_2所对应的列填充nullnull,那么对这一行null(T2)null(T_2) 就是 truetrue。如果 T={T1,T2,,Tn}\Tau = \{T_1, T_2, \ldots, T_n\},那么null(T)=null(T1)null(T2)null(Tn)null(\Tau) = null(T_1) \land null(T_2) \ldots null(T_n)null(T)=~null(T1)~null(T2)~null(Tn)\overline{null}(\Tau) = \text{\textasciitilde} null(T_1) \land \text{\textasciitilde}null(T_2) \ldots \land \text{\textasciitilde}null(T_n)

(outer union)\uplus (outer\ union): 假设表T1T_1的schema是S1S_1,表T2T_2的schema是S2S_2,那么T1T2T_1 \uplus T_2首先null extendT1T_1T2T_2是他们的schema是S1S2S_1 \cup S_2,然后做一个union all的操作。例如,假设T1T_1的schema和数据如下:

intfloat
11.1
22.2

T2T_2的schema和数据如下:

doubleint
3.33
4.44

那么T1T2T_1 \uplus T_2的schema和数据如下:

intfloatdoubleint
11.1nullnull
22.2nullnull
nullnull3.33
nullnull4.44

subsumesubsume: t1t_1 subsumesubsume t2t_2指的是t1t_1t2t_2有相同的schema,t1t_1中的nullnull少于t2t_2,并且t2t_2中的某个值不是nullnull,则t1t_1中对应的值也不是nullnull;而对于t2t_2中的非nullnull值,t1t_1中包含相同的值。例如,t1t_11,2,null)(1,2,null)t2t_2(null,2,null)(null, 2, null),则我们认为t1 subsume t2t_1\ subsume\ t_2

TT\downarrow: 如果表TT中的某个tuple被另一个tuple subsumesubsume了,则去除所有这样的tuple得到的结果。

T1T2(minimum union):T1T2=(T1T2)T_1 \oplus T_2(minimum\ union): T_1 \oplus T_2 = (T_1 \uplus T_2)\downarrow

Q1sQ2(subsume contained)Q_1 \subset_s Q_2(subsume\ contained): Q1sQ2 if t1Q1,t2Q2,t1=t2 or t2 subsumes t1Q_1 \subset_s Q_2\ if\ \forall t_1 \in Q_1, \exists t_2 \in Q_2, t_1 = t_2\ or\ t_2\ subsumes\ t_1

Join-Disjunctive Form

这里我们首先介绍几个变换规则:

Untitled.png

任何一个SPOJ的表达式,如果所有的表都满足Ti=TiT_i = T_i\downarrow,并且所有的selection条件都是nullrejectingnull-rejecting的情况下,我们可以将其转化成join-disjunctive 的形式,即:

Q=δp1(Υ1)δp2(Υ2)δpn(Υn)Q = \delta_{p1}(\Upsilon_1) \oplus \delta_{p2}(\Upsilon_2) \ldots \delta_{pn}(\Upsilon_n)

其中Υi=Ti1Ti2Tin\Upsilon_i = T_{i1} \bowtie T_{i2} \ldots \bowtie T_{in}。这里我们将每个δpi(Υi)\delta_{pi}(\Upsilon_i)称作一个term,我们可以看到每个term就是一个SPJ Query。举个例子,我们可以将如下的SPOJ的Query转化成join-disjunctive form:

V=Cock=ck(O×ok=lok(δlp<20L)))V = C \rtimes_{ock=ck}(O \times_{ok=lok} (\delta_{lp \lt 20}L)))

转化之后的结果是:

V=δlp<20ok=lokock=ck(C,O,L)δck=ock(C,O)CV = \delta_{lp \lt 20 \land ok=lok \land ock=ck}(C, O, L) \oplus \delta_{ck=ock} (C, O) \oplus C

从MV里计算Query

MV是否包含Query的所有行

为了从MV里计算Query的结果,我们首先要知道MV是否包含Query所需要的所有行。这里我们直接使用论文里证明过的一个定理:

假设Q1Q_1Q2Q_2是2个SPOJ的Query,Q1Q_1^{'}Q2Q_2^{'}分别是对应的join-disjunctive form。那么当且仅当δpi1(Υi1),δpj2(Υj2)Q2,使得δpi1(Υi1)δpj2(Υj2)\forall \delta_{pi1}(\Upsilon_{i1}), \exists \delta_{pj2}(\Upsilon_{j2}) \in Q_2^{'}, 使得 \delta_{pi1}(\Upsilon_{i1}) \subset \delta_{pj2}(\Upsilon_{j2})时,我们有Q1sQ2Q_1 \subset_{s} Q_2

从上述定理可以看出,要检测Q2Q_2是否包含Q1Q_1,只需要检查Q1Q_1^{'}的每个term是否被包含在Q2Q_2^{'}中。定理的证明比较复杂,这里我们不做详述。举一个例子来说明。假设

V=δcn<5lp<20ok=lokock=ck(C,O,L)δcn<5ck=ock(C,O)δcn<5CV=\delta_{cn\lt5\land lp \lt 20 \land ok=lok \land ock=ck(C, O, L)} \oplus \delta_{cn \lt 5 \land ck=ock}(C, O) \oplus \delta_{cn \lt 5}C

从MV中恢复Query所需要的行

假设Q1Q_1被证明包含在Q2Q_2中,那么我们要计算出Q1Q_1的值,只需要计算出Q2Q_2中对应的term的值,并且按照第一篇文章中的算法进行过滤,然后执行\oplus即可得到Q1Q_1的值。

那么对于一个SPOJ的MV,如何恢复每个term的值呢?遗憾的是不是所有的term值都可以恢复的,我们这里讨论2中情况:

  1. 当这个term的每一行都包含一个unique key的时候,我们可以通过选出不在term上null-extend的值,然后做一个去重操作即可计算出来。假设δP(Υ)\delta_{P}(\Upsilon)是我们需要找出的term,并且该term的每一行都以一个unqie key,则

    δP(Υ)=σ(πΥ.δnull(Υ)V)\delta_{P}(\Upsilon) = \sigma(\pi_{\Upsilon .*}\delta_{\overline{null}(\Upsilon)}V)
  2. 当这个term中的每一行不包含unique key的时候,如果对于该query中的其他term,存在有如下关系时,我们仍然可以计算出来。假设需要计算的term是δPi(Υi)\delta_{P_i}(\Upsilon_i),对于该query中其他的termδPj(Υj)\delta_{P_j}(\Upsilon_j),我们都满足2中条件之一: a. {Ti1,Ti2,,Tin}⊄{Tj1,Tj2,,Tjn} and{Tj1,Tj2,,Tjn}⊄{Ti1,Ti2,,Tin}\{T_{i1}, T_{i2}, \ldots, T_{in}\} \not\subset \{T_{j1}, T_{j2}, \ldots, T_{jn}\}\ and\newline \{T_{j1}, T_{j2}, \ldots, T_{jn}\} \not\subset \{T_{i1}, T_{i2}, \ldots, T_{in}\} b. {Ti1,Ti2,,Tin}{Tj1,Tj2,,Tjn}\{T_{i1}, T_{i2}, \ldots, T_{in}\} \subset \{T_{j1}, T_{j2}, \ldots, T_{jn}\},但是{Ti1,Ti2,,Tin}\{T_{i1}, T_{i2}, \ldots, T_{in}\} 是被通过一些列的cardinality reserving的join跟剩余的table join起来。

    当满足上述条件之后,我们发现δPi(Υi)\delta_{P_i}(\Upsilon_i)在MV中的duplication factor是正确的,如此我们通过如下的操作即可恢复出δPi(Υi)\delta_{P_i}(\Upsilon_i)的值:

    δPi(Υi)=πΥi.δnull(Υi)V\delta_{P_i}(\Upsilon_i)= \pi_{\Upsilon_i.*}\delta_{\overline{null}(\Upsilon_i)}V

当我们需要计算多个term的时候,需要注意避免多次扫描MV,而是通过把\oplus转换成unionunion来做。

SPOJG的改写

SPOJG的改写与上一篇文章中提到的SPJG的改写基本相同,但是在恢复tuple的时候我们要注意SPOJ的特殊部分,因此每个tuple的duplication factor应该相同,这里就不在赘述。

参考文献

  1. View Matching for Outer-Join Views, 2005
  2. Outerjoins as disjunctions, 1994