破解SQL迁移迷思:新SQL特性让存储过程平迁湖仓更轻松
DataHot 速览
本文针对数据仓库迁移到湖仓时,遗留存储过程等过程化SQL逻辑难以迁移的痛点,以Oracle迁移场景为例,展示如何在Databricks Lakehouse上利用新SQL特性,将原有业务逻辑逐行翻译而非用Python/Spark重写。文章强调这样既保留原逻辑,也让SQL团队继续维护自己的业务逻辑,降低迁移门槛。
为什么值得关注:数据平台迁移中存储过程重构是常见难点,本文提供无需重写的平迁思路,对湖仓迁移实践有参考价值。
本文目录 8 节
译文
AI 逐段翻译在你的仓库某处,数百个存储过程每晚醒来,悄无声息地维持着业务运转。它们是多年前由一批早已离职的 SQL 开发人员编写的。它们有嵌套游标,动态创建临时表,将跨多个表的更新捆绑在单个事务中。而在第 47 行附近,有一条注释简单写着:“不要更改此内容。”如今已无人完全理解这些过程,但每个人都依赖它们。收入仪表板、财务结算、运营报告,所有这一切,都以某种方式追溯到这些过程化 SQL 业务逻辑层。
将数据迁移到湖仓一体已得到充分理解。瓶颈在于任何数据仓库迁移的过程化核心:存储过程、事务处理、临时表、控制流,以及大量企业仍然依赖 SQL 技能的事实。每次迁移即将开始时,这些过程就成了所有人首先指出的问题:“除非我们能以最小改动运行这些过程,否则无法迁移。我们的企业仍然重度依赖 SQL。”
因此,我们决定采用一个你可能正在考虑的场景,一个我们在迁移中见过的复合过程,并在 Lakehouse 上逐步演示。此示例基于 Oracle 迁移场景,但可应用于任何数据仓库(遗留或基于云)。
获取原始业务逻辑
此示例过程处理每日订单。它将未处理的订单暂存到临时表中,根据客户主数据验证它们,循环处理失败记录以单独记录每个拒绝原因,然后更新区域收入汇总并将所有订单标记为已处理,整个过程在失败时回滚的事务中进行。
一个不可中断的夜间作业。
此前,迁移意味着用 Python 和 Spark 完全重写。数周的工作,新出现的 bug,以及一个无法再维护自身业务逻辑的 SQL 团队。
我们没有重写,而是翻译了它。
现在在 Databricks 上奠定基础
每个过程都以签名和安全网开始。遗留代码将主体包裹在 BEGIN ... EXCEPTION ... END 中。Databricks 改用 DECLARE EXIT HANDLER FOR SQLEXCEPTION;同样的思路,语法略有不同。假设会话中已设置适当的目录和架构。
最大的区别不在于代码,而在于部署后会发生什么。在 Databricks 上,过程注册在 Unity Catalog 中,获得访问控制、列级血缘,并在所有工作区中可发现。而在当前系统中,它存在于一个只有三个人知道密码的架构中。
| 遗留 | Databricks |
| CREATE OR REPLACE PROCEDURE name IS | CREATE OR REPLACE PROCEDURE [IF NOT EXISTS] <catalog>.<schema>.<procedure_name> ( [ procedure_parameter [, ...] ] ) [ characteristic [...] ] LANGUAGE SQL SQL SECURITY { INVOKER | DEFINER } AS BEGIN |
| v_id NUMBER; 在 BEGIN 之前 | DECLARE v_id INT; 在 BEGIN 内部 |
| EXCEPTION WHEN OTHERS THEN | DECLARE EXIT HANDLER FOR SQLEXCEPTION |
参考:docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-ddl-create-procedure
然后我们处理了临时表:数据仓库迁移中的轻松胜利
原始过程创建两个 临时表 用于暂存和验证失败。它们是其余逻辑所依赖的临时空间。
在 Databricks 上,这成为迁移中最简单的部分之一。无需 EXECUTE IMMEDIATE,无需 ON COMMIT PRESERVE ROWS。会话范围的 CREATE TEMP TABLE 是直接替代品,但有一个小注意事项:尚不支持 CREATE OR REPLACE TEMP TABLE,因此如果需要同一会话中可重新运行,请先删除。
参考:docs.databricks.com/aws/en/tables/temporary-tables
游标是难点——至少我们曾这么认为
这是每个人都认为需要重写的部分。原始过程逐个循环验证失败,拒绝每个错误订单,并记录原因。典型的游标模式,几十年的遗留(例如 Oracle)肌肉记忆。
自 Runtime 18.1 起,Databricks 的 SQL 脚本原生支持游标:OPEN、FETCH 和 CLOSE。 %NOTFOUND 属性变为 CONTINUE HANDLER FOR NOT FOUND。循环标签和 LEAVE 替代 EXIT WHEN。
脚本逻辑并不复杂
条件检查(如果没有要处理的行则跳过并记录)几乎未变。 SELECT ... INTO 变为 SET var = (SELECT ...)。其余完全相同。
我们的 SQL 脚本支持完整的过程化工具集:IF/ELSE、WHILE、FOR、LOOP、REPEAT、LEAVE、ITERATE、SIGNAL/RESIGNAL。如果你的代码库包含 Teradata BTEQ 脚本,其中 .GOTO 和 .LABEL 指令映射到使用 LEAVE 和 ITERATE 的带标签循环。
参考:docs.databricks.com/aws/en/sql/language-manual/sql-ref-scripting
事务是关键时刻
这是最后一块,使迁移真正可行。原始过程更新 regiona_revenue,将订单标记为已处理,并记录批次。如果任何部分失败,全部回滚。
在遗留系统上,这是带有显式 COMMIT 的隐式事务。在 Databricks 上,BEGIN ATOMIC ... END 提供相同语义,成功自动提交,失败自动回滚,并带有一个显著优势:行级冲突检测。并发批次写入同一表仅当触及相同行时才冲突。例如,Oracle 和 Snowflake 都使用表级锁,这强制串行执行。
MERGE 语句可以原样迁移到 Databricks。显式 COMMIT 消失,因为 BEGIN ATOMIC 处理了它。团队不再担心并发批次作业相互踩踏。
采用此模式时的两个实用提示:
- 原子块内定义的每个表都必须启用 catalogManaged 表特性。你可以就地启用现有 Delta 表:ALTER TABLE <name> SET TBLPROPERTIES('delta.feature.catalogManaged'= 'supported');
- BEGIN ATOMIC 应位于顶层——在 SQL 脚本、笔记本单元格或 SQL 作业任务中。
参考:docs.databricks.com/aws/en/transactions/
完整的迁移后过程
相同的业务逻辑。相同的控制流。由 Unity Catalog 管理。
要在事务中运行它,请包装调用:
我们学到的经验
这些程序的迁移时间可缩短 50-75%,即使对于依赖大量 PL/SQL 包的复杂存储过程也是如此。这种效率源于机械翻译过程,该过程保持原始业务逻辑,确保 SQL 团队能无缝继续进行维护工作。除了迁移本身,团队还获得了强大的新优势:一个统一平台,其中相同的数据用于驱动仪表板、机器学习模型和 AI 计划。
要想知道你的过程能否转换,唯一的方法是尝试一个。选择你批次中最小的存储过程,最好是一个没人喜欢调试的。在工作区创建迁移项目,并开始使用 Agentic Code Convertor!
这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏