BigQuery中时间单位列分区表全外连接后WHERE date过滤未触发分区Pruning的问题咨询
这确实是BigQuery的预期行为,背后的核心原因和分区裁剪(partition pruning)的工作机制有关。让我给你拆解一下:
首先,你用了FULL OUTER JOIN USING(date),这里的date列是连接后的合并列——它的值可能来自表A的date,也可能来自表B的date。当你用WHERE date = '2021-01-01'过滤时,BigQuery没办法在执行连接操作之前就确定哪些分区的数据会满足这个条件:毕竟全外连接会保留两个表中所有不匹配的行,BigQuery得先扫描两个表的所有分区完成连接,之后才能过滤出date等于目标值的行,这就导致分区机制完全没起作用,处理量自然很大。
而当你用WHERE A_date = '2021-01-01'或者B_date = '2021-01-01'时,BigQuery可以分别对表A或表B单独做分区裁剪(只扫描date='2021-01-01'的分区),再执行连接,所以处理量会小很多;当你同时过滤两个列时,两个表都只会被扫描目标分区,连接后的数据量极小,这就是你看到65.6KB的原因。
解决办法
有两种实用的方式可以让分区裁剪生效,同时保留你需要的逻辑:
直接过滤两个表的分区列
既然A_date就是A.date,B_date就是B.date,你可以直接写:SELECT date, A.date AS A_date, B.date AS B_date FROM A FULL OUTER JOIN B USING(date) WHERE A.date = '2021-01-01' AND B.date = '2021-01-01'这和你最后那个测试条件效果完全一致,BigQuery会对两个表都做分区裁剪,只处理目标日期的分区。
先过滤分区再做连接
如果你的业务逻辑需要保留全外连接的特性(比如某个表在目标日期没有数据时,仍然要显示对应的空行),可以先通过CTE分别筛选出两个表的目标分区,再执行连接:WITH filtered_A AS ( SELECT * FROM A WHERE date = '2021-01-01' ), filtered_B AS ( SELECT * FROM B WHERE date = '2021-01-01' ) SELECT date, A.date AS A_date, B.date AS B_date FROM filtered_A FULL OUTER JOIN filtered_B USING(date)这种写法会让BigQuery优先完成分区裁剪,只加载两个表的
2021-01-01分区数据,再执行连接,处理量会大幅降低。
总结一下:全外连接场景下,直接过滤连接后的合并列无法触发分区裁剪,必须明确针对每个表的分区列做过滤,或者提前筛选分区再连接。
内容的提问来源于stack exchange,提问作者Helge

