WinMagic : Subquery Elimination Using Window Aggregation

简介: 这篇精干的paper介绍了IBM DB2基于window function对相关子查询进行解相关的等价变换。

这篇精干的paper介绍了IBM DB2基于window function对相关子查询进行解相关的等价变换。

基本概念

例如SQL:

SELECT emp_id, emp_name, dept_name
FROM employee E, department D
WHERE E.dept_num = D.dept_num AND
E.state = ‘CALIFORNIA’ AND
E.salary > (SELECT AVG(salary)
FROM employee E1
WHERE E1.dept_num = D.dept_num);

其原始的语义就是对外层查询的每一行结果(E join D),获取其相关列D.dept_num的值(d),代入到相关子查询中,内层所有在E1.dept_num上具有相同对应值(d)的行,作为满足相关条件的过滤结果,计算一次聚集。然后基于聚集的结果对外层的该行进行过滤判断(E.salary > AVG(sal))。

DB2在之前的paper中提到了一种magic de-correlation的变换,利用了所谓的magic set。根据相关子查询的语义,将外表相关列的每一个唯一值组成一个magic set,拉入到内层来与内表join,join的结果其实就等同于外表每个相关值所对应的内表行,组合在了一起。这样在对各个组各自聚集,聚集的结果是以外表的相关列为分组的,因此就完成了去相关,计算结果再与外表join就可以了。

v2-50dd0eb74f20f9d9d5945f4122d2b318_b.png

从上图可以看到,提取出magic set与内表做join的过程,就等价于用外表每一个相关值在内表依次进行过滤的过程,这样计算出的每个分组聚集结果就是每次带入相关value计算的聚集结果。将结果与外层再基于相关列做join,等于还原了针对外表每一行都要计算的语义,恢复原始结果。

WinMagic也是这个思路,只是在满足某些特定条件下,可以更进一步,去掉内/外层中的common table,使其只需要计算一次就可以了。

关于window function的介绍这里就不赘述了,可以参考MySQL reference的介绍 。这里就先假设大家都了解window function了。

WinMagic Transformation

前提条件

  1. 子查询中有aggregation,这样window function才有意义。
  2. 由于改变了整个查询的执行流程,不能存在有side effect的函数,也就是在不同执行流程下结果会不同的函数,例如RAND()这样的function。
  3. subquery中不能有Top-N这种截断性的操作,会破坏内外层数据的一致性
  4. 内层aggregation具有等价的window function,例如min/max/sum/count,而没有aggr(distinct ...)的聚集计算(window function不支持distinct)。
  5. 外层和内层之间,存在subsume关系!!(外层在join table/condition上,包含内层),这个条件可以放宽:

内层如果有其他非相关表,必须是lossless join,保证内层相关表的数据不会由于join丢失/增加

内层相关表可以有更多的单表限定条件,在转换时,需要通过在window aggregation时,利用CASE …. WHEN这样的条件判断,来保证数据能够按照原语义被过滤。

关于这种特殊情况,我们一会可以通过示例看下。

转换过程

  1. 内层aggregation -> window aggregation,并以内层相关列作为partition by列。
  2. 把外层相关表压入到内层中,与内层表做join,这是在解相关,但这里要区分对待:

2.1 如果外层相关列是个主键/唯一键列,则拉入内层,也不会导致内表数据量增加(内层每一行,最多join上外层一行),由于外层中也有这个内层表以及表上的过滤条件(subsume关系)。内层过滤掉的数据外层也一样过滤掉了,数据内外是一致的,这时window partition列是[内层相关列]

2.2 外层相关列不是主键/唯一列,拉入内层后,内层表数据量会由于join而变多(重复的外层相关列),此时仍需要保证”对外表每一行,内表做一次聚集“的语义,因此window partition列是 [外表主键,内层相关列]。

3. 由于外层subsume内层,而且内层包含了所有common table,这时外层的common table就可以去掉了,只剩下非相关table,再与子查询去相关后的derived table做join。这样就去掉了对common table的重复执行。

用一个具体的示例来说明这个过程:

SELECT SUM(l_extendedprice) / 7.0 AS avg_yearly
FROM tpcd.lineitem, tpcd.part
WHERE p_partkey = l_partkey AND
p_brand = 'Brand#23' AND
p_container = 'MED BOX' AND
l_quantity <
(SELECT 0.2*avg(l_quantity)
 FROM tpcd.lineitem
 WHERE l_partkey = p_partkey);

观察以上query,内外层都包含tpcd.lineitem这个表,相关条件在l_partkey = p_partkey。

image.png

如上示例符合2.1中的情况,即相关列是外层表part的主键列,因此window function只需要partition by p_partkey。

1将外层的part推入内层,2与lineitem做join,等于在外层/内层,都有lineitem join part这个操作,产生了相同的结果集(外层由于可能有更多条件,可能数据会更少,但没有关系,可以把这些额外条件认为在join之后再计算)。3在join结果上基于相关列做partitioned window function的计算,实际上就是基于每个相关值,计算一个局部聚集结果。4将聚集结果作为附加列添加在join结果的最后。5由于内外层subsume的关系,内层join的结果也是外层join的结果,并附加了基于相关性计算的内层聚集结果作为额外一列。这时再应用外层其他条件,对结果继续过滤就可以了。

这里把外层推入后,最主要的一个优化就是内层part join lineitem的结果就是外层common tables join的结果。因此只需要在内层计算一次,结果以derived table的形式复用到外层就可以了,避免了内外层的重复计算。

转换后的query变为

WITH WinMagic AS
(SELECT l_extendedprice, l_quantity,
avg(l_quantity)over(partition by p_partkey)
AS avg_l_quantity
FROM tpcd.lineitem, tpcd.part
WHERE p_partkey = l_partkey and
p_brand = 'Brand#23' and
p_container = 'MED BOX' )
SELECT SUM(l_extendedprice) / 7.0 as avg_yearly
FROM WinMagic
WHERE l_quantity < 0.2 * avg_l_quantity;

对于2.2的情况,思路是完全一样的,只是原始语义中,是外层的一行带入到内层做一次聚集,因此应该仍保证这一点,在内层算window aggregation时考虑外层主键 + 内层相关列。

前面提到了如果内层有比外层更多的过滤条件怎么办?仍然从语义的角度出发,也就是在内层做join时,有些行会被内层的额外条件过滤掉,但是为了满足内外层数据一致的特性,仍然是需要原样做join的,只是那些过滤掉的行不参与window aggregation的计算就可以了。

可以通过类似sum(case when pred is true then value else NULL) 这样的方式,来保证不满足predicate的行不计入聚集结果中。

image.png

通用形态

将上面描述的内容泛化,WinMagic的通用形式如下:

image.png

  1. T1, T2, T3, T4是一组表(table/view)。
  2. 在内层,T1是内层非subsume的无关表,与T3(相关表)必须是lossless join。
  3. T2是外层无关表,不能与T4(相关表)直接join。这里应该是为了保证T3 Join T4这个内外层操作在数据上的一致性。

那么转换时,T4 pushdown 到子查询中计算window function,完成解相关+转为derived table,外层的T3 join T4就不需要了,这个derived table(WinMagic)与T2 join即可。

image.png

总结

WinMagic的本质是,外层已经包含了内层的相关表,所以可以直接复用下推后内层T3 join T4的结果来计算聚集,以window function的形式将聚集结果作为附加列加在结果上,然后外层基于这个结果再进行后续计算。

可以看到,这个变换还是非常简单的。目前PolarDB也实现了这个win magic的等价变换,基于完全相同的思路。

目录
相关文章
|
SQL 关系型数据库 数据库
RDS入门——Excel文件转存到RDS数据库实践
本实验将帮助您快速掌握RDS产品的实例开通,熟悉RDS产品的常用功能与基础操作,完成云上数据库搭建。
|
3月前
|
存储 人工智能 运维
AI Agent 会话与长期记忆存储:阿里云 Lindorm 一体化方案
AI Agent 的"记忆"是决定其智能水平的核心要素,需要同时存储短期会话上下文、中期会话历史和长期跨会话知识。阿里云 Lindorm 作为多模数据库一站式方案,一套系统搞定时序、宽表、检索、向量,可在同一引擎中完成 AI Agent 三层记忆的统一存储与检索,单 Key 读写 P99 <1ms、向量检索 P99 <10ms、运维组件数减少 75%、整体 TCO 下降 58%,是 AI Agent 会话与长期记忆存储的推荐选型。
341 0
前后工作效率差别有点大
用户反馈:初期体验尚可,但使用数日后发现大模型响应变迟钝,同一任务反复处理,不仅大量消耗token,还显著降低效率,影响使用体验。
|
1月前
|
SQL 监控 关系型数据库
磁盘98%告警,ibdata1占了320G:五个大户排查记录
以凌晨磁盘告警事故切入,逐一排查binlog、InnoDB表空间、undo日志、临时表、慢日志五个磁盘大户,覆盖MySQL 8.0的undo表空间管理和TempTable引擎变化,附自动清理脚本与监控配置
|
10月前
|
SQL 数据可视化 大数据
我是谁?我从哪来?我要到哪去?——聊聊数据血缘分析的“前世今生”
我是谁?我从哪来?我要到哪去?——聊聊数据血缘分析的“前世今生”
680 11
|
3月前
|
人工智能 运维 安全
Hermes Agent 核心必学:SubAgent 子代理的 5 个实战技巧,多任务处理效率翻倍
Hermes Agent SubAgent子代理完整教程:掌握delegate_task并行委派、上下文隔离与多任务处理核心能力,提升开发效率。
940 1
|
10月前
|
SQL 关系型数据库 MySQL
释放数据潜能,加速业务创新 —— Dataphin 5.4 新增删改API功能
Dataphin 5.4推出数据增删改API功能,支持通过配置SQL快速生成安全、可管理的CRUD接口,覆盖AI编程、数据集成、低代码等场景,降低开发成本,提升数据治理与安全性,助力企业高效释放数据价值。
579 0
|
3月前
|
人工智能 弹性计算 自然语言处理
阿里云云聚AI活动:汇集爆款AI产品,热门模型专属权益及优惠券助力企业创新加速
阿里云"云聚AI长效权益"活动构建了从模型到应用的全链路AI赋能体系。用户可免费领取60元AI新品尝鲜礼包,并通过Token Plan订阅计划(198元/月起)灵活调用150+款模型,覆盖Qwen、DeepSeek、Kimi等主流大模型。活动推出Qoder CN编程智能体、万小智AI建站等应用首月0元体验,精选AI产品组合购覆盖90%+场景,低至52元起。重磅OPC创新助力计划提供最高100万元Token补贴,支持先用后返。叠加Qwen3.7-Max限时5折、按量达标返券等多重优惠,全方位满足个人开发者零成本尝鲜到企业级AI深度落地的全场景需求。
|
4月前
|
人工智能 运维 网络协议
ZeroNews CLI 一条命令搞定内网穿透
ZeroNews CLI是其4.0版本推出的命令行工具,支持一键认证、添加HTTP/TCP映射、查状态、启停服务等,无需浏览器操作;深度适配AI Agent与自动化场景,助力开发、运维高效完成内网穿透。(239字)
|
5月前
|
人工智能 供应链 安全
2026 年网络威胁态势与智能防御体系研究 —— 基于 Check Point 威胁情报报告
本文基于Check Point 2026年4月威胁情报,系统剖析AI驱动攻击、供应链入侵、高危零日漏洞及定向威胁新趋势;提出以威胁情报驱动、AI检测、漏洞闭环、零信任与供应链安全为核心的一体化防御体系,并提供可落地的检测代码、配置与响应流程。(239字)
2056 13