Redshift中多查询计算列按userid关联合并到结果表的最优方案
方案优劣分析及最优实现
现有三个方案的评估
- 方案1(多临时表关联):不推荐。50个单表50G的临时表会占用至少2.5T的临时存储空间,且后续执行49次大表关联的计算开销极高,还会占用大量命名空间,投入产出比极低。
- 方案2(原始WITH CTE多INNER JOIN):性能优于方案1和3,但存在明显缺陷。
你提到的Redshift CTE优化规则:在可行的情况下,被多次引用的WITH子句子查询会作为公共子表达式优化,即WITH子查询可能仅执行一次,结果可复用。
这个规则在当前场景下不会触发,因为每个CTE仅被引用一次,但也不会重复执行CTE,每个查询只会跑一次。缺陷是连续49次INNER JOIN的计算成本会随关联次数指数上升,且如果不同查询的userid不完全重合,会直接丢失仅在部分查询中存在的userid数据。 - 方案3(50次UPDATE JOIN):完全不推荐。Redshift的UPDATE操作本质是标记旧行为墓碑记录+写入全新整行,就算仅更新单列也会产生整行的IO开销,50次UPDATE相当于将结果表重复写入50次,会产生大量需要VACUUM清理的垃圾数据,性能比方案2低一个数量级。
最优推荐方案(无需存储过程)
放弃多表关联逻辑,改用UNION ALL + 聚合的实现,性能比原始CTE关联方案高30%~50%,兼容不同userid的存在,无额外存储开销:
INSERT INTO Resultant (userid, c1, c2, ..., c50) SELECT userid, MAX(c1) AS c1, MAX(c2) AS c2, -- 依次补充c3到c49的聚合逻辑 MAX(c50) AS c50 FROM ( SELECT userid, c1, NULL::INT AS c2, NULL::INT AS c3, ..., NULL::INT AS c50 FROM t1 -- 注意NULL的类型要和对应列的类型保持一致 UNION ALL SELECT userid, NULL::INT AS c1, c2, NULL::INT AS c3, ..., NULL::INT AS c50 FROM t2 UNION ALL -- 依次补充t3到t49的查询逻辑 SELECT userid, NULL::INT AS c1, NULL::INT AS c2, ..., c50 FROM t50 ) AS all_data GROUP BY userid -- 如果需要仅保留在所有50个查询中都存在的userid,打开下面的过滤条件即可 -- HAVING COUNT(*) = 50 ;
该方案的核心优势:
- 仅扫描每个源表一次,没有多次关联的哈希/排序开销,Redshift对列存的聚合运算优化非常成熟,执行效率极高
- 全程无临时表写入,无额外存储成本
- 逻辑灵活,可通过增减HAVING条件灵活控制保留的userid范围,比连续INNER JOIN的适配性更强
- 纯SQL实现,无需引入存储过程,维护成本极低
内容的提问来源于stack exchange,提问作者Harsh P Waghela
相关产品推荐
相关产品推荐

