ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

达梦数据库同义词“半支持”排查:自定义类型与存储过程使用踩坑与绕行方案

2026/9/16 4:15:20 拓冰建站 浏览量
达梦数据库同义词“半支持”排查:自定义类型与存储过程使用踩坑与绕行方案 先说个背景我最近在帮客户做国产化数据库迁移把一套基于Oracle的老系统往达梦DM8上搬。数据库对象大部分靠迁移工具转过来了剩下的手工作业脚本里卡我时间最久的不是什么复杂存储过程、也不是什么奇葩函数而是一个看起来人畜无害的小功能——同义词。具体现象很拧巴执行CREATE SYNONYM给自定义类型建同义词成功给存储过程建同义词也成功。但真正去用的时候比如在另一个存储过程里用同义词声明一个变量或者直接CALL一个由同义词指向的过程数据库立马翻脸报错提示大意是“无效的同义词”或“对象不存在”。一开始我还以为是自己权限没配好来回折腾了大半天最后才确认这根本不是权限问题而是达梦的同义词机制对自定义类型和存储过程执行了一个“半支持”策略允许你把别名建出来但使用环节并没有完整放开。这篇文章就把我踩坑、排查、绕行的完整过程写出来。尤其是正在做Oracle到达梦迁移、或者刚接触达梦开发的朋友应该能帮你少走不少弯路。1. 问题现象还原同义词建得成用不成到底长什么样1.1 最小复现自定义类型和存储过程同义词的完整测试先说清楚我当时是怎么复现的。为了隔离问题我新建了一个测试用户专门最小化验证不掺任何业务逻辑。第一步建一个测试用的自定义类型CREATE OR REPLACE TYPE T_EMP_INFO AS OBJECT ( EMP_ID INT, EMP_NAME VARCHAR(50) );第二步建一个入参用到这个自定义类型的存储过程CREATE OR REPLACE PROCEDURE P_GET_EMP(EMP T_EMP_INFO) AS BEGIN -- 仅做测试不写实际业务逻辑 NULL; END;第三步分别给类型和过程创建同义词CREATE SYNONYM S_EMP_INFO FOR T_EMP_INFO; CREATE SYNONYM S_P_GET_EMP FOR P_GET_EMP;这三步执行下来数据库全部返回成功没有任何警告。然后问题来了。我在一个新的存储过程里尝试用同义词声明变量CREATE OR REPLACE PROCEDURE P_TEST_TYPE AS V_EMP S_EMP_INFO; -- 这里用同义词声明自定义类型变量 BEGIN NULL; END;在达梦管理工具里点编译直接报错提示“未定义的类型”或者“无效的标识符”具体文本记不太清了但意思就是解析不到S_EMP_INFO这个类型。用同义词直接调用过程也一样CALL S_P_GET_EMP(NULL);或者EXEC S_P_GET_EMP(NULL);同样报错说找不到对象。但你把调用改成原始对象名比如CALL P_GET_EMP(NULL);一切正常。这就是最典型的“能建不能用”DDL阶段放行DML、编译阶段卡死。1.2 为什么说这是半支持能建能用的对象类型边界为了搞清楚到底哪些对象类型的同义词在达梦上是“真支持”哪些是“假支持”我做了一组对照实验覆盖了表、视图、序列、存储过程、函数、自定义类型。结果整理成一张表大概是这样的对象类型创建同义词使用同义词说明表支持支持最稳定SELECT、INSERT、UPDATE、DELETE 都能用视图支持支持和表同义词行为基本一致序列支持支持NEXTVAL、CURRVAL 可以正常取到存储过程支持部分不支持具体看版本和调用方式我这边直接CALL失败函数支持部分不支持在SQL里直接引用函数同义词时问题更明显自定义类型支持不支持在PL/SQL里声明变量时报未定义类型这个表格的意义在于它告诉我们一个很重要的事实同义词在达梦内部不是统一的一揽子能力而是分对象类型、分使用场景逐步开放的。表、视图、序列这类“SQL语句直接操作的对象”是优先级最高的支持和Oracle行为基本一致。但存储过程、函数、自定义类型这些需要经过PL/SQL编译器和符号表解析的对象就存在明显的短板。所以你在做方案评估时不能笼统地问“达梦支不支持同义词”而应该问“我要用的这个对象类型它的同义词在哪个版本、哪个使用场景下是通的”。我这次就是吃了“想当然”的亏。1.3 快速确认你是否踩了同一个坑如果你也遇到了类似问题先别急着翻权限、查网络按下面三个步骤做一遍基本能定位。第一看错误出现的时点。如果你只是在创建同义词时成功、一调用就报错那大概率不是权限问题而是对象解析链路的问题。权限问题通常在创建时就报“无权限”或者在调用时明确提示“权限不足”不会给你“对象不存在”这种模糊提示。第二做一个对照实验。在同一个用户下建一张测试表给它建同义词然后SELECT * FROM 同义词名;如果这个操作正常而存储过程、自定义类型的同义词调用报错那基本可以断定和对象类型有关而不是整体同义词特性失效。第三查版本和补丁。达梦同一个大版本下不同小版本的兼容性差异可能很大。我这边测试环境是DM8的某个版本后续打补丁之后部分场景确实有改善。所以出现问题时顺手记一下当前版本号找原厂技术支持时也能快速对齐信息。2. 达梦同义词的解析机制为什么允许创建不等于允许使用2.1 同义词到底是个什么东西在展开分析之前有必要先回到最基础的问题同义词到底是什么。说白了同义词就是数据库里的一个别名映射。它在数据字典里就是一条记录记录了三样核心信息同义词叫什么名字、它指向哪个用户下的哪个对象、这个对象是什么类型。以达梦为例你创建同义词时数据库往系统字典里写入了一条映射记录。之后你在SQL语句里写这个别名数据库解析到这个名字时会去字典表里查映射找到真实对象再翻译过去执行。这个“翻译”的过程业内一般叫“名字解析”或“同义词展开”。用生活里的类比同义词相当于给你的同事起了一个外号。你可以在公司通讯录里记录这个外号但真正要找到这个人、给他发邮件通讯录系统得能把这个外号映射回他的真实姓名和工号。如果系统只是在通讯录里存了一条“外号真名”的记录但发邮件功能没有接入这个映射能力那就会出问题。达梦现在的情况就很像是“通讯录能存外号但部分功能模块根本没接外号解析”。2.2 创建与使用用的是两条不同的解析路径为什么会这样关键在于达梦对“创建同义词”和“使用同义词”这两个动作内部的解析路径是完全不同的。创建同义词时数据库做的事情比较简单。它在DDL解析阶段把你的CREATE SYNONYM语句拆开拿到同义词名字和目标对象名然后往字典表里插入一条映射记录。这个阶段数据库不会强制校验目标对象是否存在、是否可用很多时候是“先记下来用到再说”。这也是为什么你能成功创建指向存储过程和自定义类型的同义词——创建动作本身太“轻”了几乎没有做深入的对象类型检查。使用同义词时情况就完全不一样了。无论你是执行SELECT、CALL还是在PL/SQL代码块里声明变量数据库都需要把语句送进对应的解析器。对于表、视图这类对象SQL解析器会做一个完整的“对象名展开”流程读到同义词名查字典找到基表替换成真实对象名继续后续的语法分析和优化。这条路在达梦里走得非常通顺因为这是所有DML操作的基础能力。但对于存储过程、自定义类型这类对象数据库要做的是在PL/SQL编译器的符号表里去解析。符号表解析这套逻辑在达梦内部跟SQL解析器是两套体系它去识别一个过程名或者类型名时对同义词的展开支持并不一致。我在测试中发现同样的同义词在动态SQL里通过字符串拼接去调用有时候反而是通的但在静态SQL里直接写反而不通。这进一步说明真正影响解析结果的不是同义词本身而是调用语句走了哪套解析器。2.3 达梦对同义词展开的对象类型差异达梦官方文档里对同义词的描述通篇强调的核心能力是“屏蔽对象名及其属主信息”。这句话听起来很通用但实际落到代码层面不同对象类型的实现深度完全不同。我自己的理解是表、视图、序列这类对象达梦在底层实现时就把同义词展开的钩子留在了最基础的SQL引擎层只要语句能进SQL引擎同义词就能被翻译。而存储过程、函数、自定义类型这些对象达梦的设计重心在于兼容Oracle的PL/SQL语法和调用语义同义词这个特性在PL/SQL引擎里属于后来补的能力补丁覆盖不够全面时就容易出现“创建成功、用起来报错”的尴尬。此外达梦对同义词还有一个限制就是同义词不能基于同义词创建不支持链式引用。这个限制Oracle也有但实际影响范围更广因为一旦某些基础对象是通过同义词暴露的下游再想基于这个同义词继续做映射就完全走不通了。2.4 权限校验也会放大这个坑同义词这个功能还有一个容易让人误判的地方权限校验。你必须明白同义词本身不是一个实体对象它只是一个指向实体对象的别名。所以数据库做权限校验时最终校验的是你对同义词背后的“基对象”有没有权限而不是对“同义词”本身有没有权限。Oracle里你对同义词做GRANT SELECT ON 同义词名 TO 用户实际上是把这个权限挂到了基表上。达梦的机制并不完全一样我这次排查时也踩过这个坑我以为给应用用户授权的对象名从基表换成同义词就行结果运行时报“对象不存在或无权访问”。最后取消同义词授权直接给基对象授权问题才解决。如果遇到权限配置不当的情况达梦有时不会明确告诉你“权限不足”而是给一个“对象不存在”的报错。这就非常误导人了——你会以为问题出在对象解析上然后花大量时间在建同义词、删同义词、换名称上打转事实上只要把基对象的权限授过去就完事了。2.5 版本与补丁很关键但容易被忽略最后还要说一个很容易被忽略的因素版本。我查了网上一些讨论帖发现不少人抱怨达梦的同义词“功能残缺”但下面偶尔有人回复“我这边版本打补丁后可以用”。这说明达梦自己在持续补这块不同小版本之间的行为差异确实存在。所以我的建议是遇到同义词问题时先看当前版本号再到官方文档、技术社区或者咨询原厂支持确认这个版本对存储过程、函数、自定义类型的同义词支持情况如果当前版本确实存在缺陷评估升级补丁或新版本的成本优先尝试用打补丁解决而不是急着改业务代码。我之前排查过程中就是因为版本信息没摸清在错误的方向上多花了两天时间。这一点大家一定要注意。3. 工程上的绕行方案在尽量不动业务代码的前提下把活干完问题定位清楚之后摆在面前的就是怎么绕过去。严格来说同义词这块没有一劳永逸的完美方案最终都得结合项目实际情况做一些取舍。下面我把自己尝试过、验证过有效的方法按优先级列出来供大家参考。3.1 最简单的方案别用同义词直接写全限定名如果你对代码的可移植性要求不是特别高最省事的方案就是放弃同义词直接在SQL和存储过程里写全限定名也就是“模式名.对象名”的完整写法。比如原来应用里调用S_P_GET_EMP现在改成TESTUSER.P_GET_EMP原来用S_EMP_INFO声明变量现在改成TESTUSER.T_EMP_INFO。眼不见心不烦彻底绕开同义词解析这个坑。这么做的优点非常突出改动简单直接不存在额外的对象依赖解析路径最短不会因为同义词展开失败导致莫名其妙的报错写完就能上线不需要等补丁、不需要原厂介入。缺点也很明显业务代码里到处都是模式名后续如果做模式重命名或数据库迁移改起来很痛苦和Oracle原来“同义词屏蔽模式名”的设计初衷背道而驰。所以我的建议是这个方案适合小规模应用或者同义词只占很小比例的场景。如果项目体量大、后续还有多轮迁移不建议一上来就选这条路。3.2 转发对象方案用薄封装替代同义词如果全限定名的方式实在接受不了可以试试“转发对象”的思路。原理很简单既然同义词用不了那我干脆创建一个同名的新对象内部帮你转发到真实对象。比如存储过程的问题我可以建一个跟同义词同名的存储过程形式参数和原始过程保持一致函数体里只做一件事——调用真实的过程CREATE OR REPLACE PROCEDURE S_P_GET_EMP(EMP T_EMP_INFO) AS BEGIN TESTUSER.P_GET_EMP(EMP EMP); END;这样应用层还是调用S_P_GET_EMP但对达梦来说它解析到的是一个普通存储过程不存在同义词展开的问题。手写转发逻辑也不复杂只是维护成本多了一层以后原始过程签名变了转发过程也要跟着变。自定义类型就比较麻烦了因为类型不像过程那样可以用“调用转发”来模拟。在这个案例里我的实际做法是在存储过程的形参、变量声明里保持使用原始类型名T_EMP_INFO只在应用层通过一个常量或别名配置去控制。如果项目确实有“统一类型名”的诉求并且达梦版本支持包PACKAGE功能可以考虑把自定义类型放进包里然后通过“包名.类型名”的方式去引用这也是一种解耦思路。不过“薄封装”方案也有它的边界。如果同义词指向的对象数量很大你要写一堆转发过程代码生成倒是小事后续维护很容易漏。所以我一般只在少量关键对象上使用这个方案不会大规模铺开。3.3 脚本化改造Oracle迁移存量脚本的同义词处理规则对于从Oracle迁到达梦的存量系统里面通常有一堆现成的同义词对象。这些同义词不能简单扔掉但也不能指望全部原样保留。我的经验是按对象类型分类处理制定一套简单的规则。具体来说我建议把同义词分成三类第一类是表、视图、序列的同义词。这一类基本可以保留迁移后做个验证就行。验证方法也很简单写一个脚本遍历所有同义词对每个同义词执行一个最基本的查询或取值操作能过说明没问题。第二类是存储过程、函数、包的同义词。这一类必须先做小范围验证看当前达梦版本能否正常调用。如果验证通过就保留如果通不过就用3.2的转发对象方案替代。第三类是自定义类型的同义词。按照我目前的测试结果直接用大概率是不行的。建议一是替换成真实类型名二是通过包封装来模拟取决于你的业务对类型解耦的迫切程度。这个改造过程可以脚本化。我当时写了一套简单的PL/SQL脚本查询系统字典表里所有同义词的定义然后根据TABLE_OWNER和TABLE_NAME关联到对象类型最后生成一份同义词分类清单交给开发人员逐项处理。别小看这一步它能把原本混沌的存量资产理清楚也避免上线前一天发现还有漏网的同义词在报错。3.4 升级与求助原厂什么情况下值得走这条路工程上还有个选项就是推动数据库版本升级或打补丁。我在前文提到达梦不同小版本对同义词的支持力度不一致。如果你的项目还没上线数据库环境还在搭建阶段那尽量装上最新稳定版本能少踩很多坑。如果已经上线了碰到的问题正好是某个已知缺陷那就评估一下打补丁的风险和成本。怎么判断是不是已知缺陷最直接的办法就是找原厂技术支持把最小复现场景、数据库版本号、报错信息整理清楚发过去问。我当时最后也是通过这种方式确认了是版本限制并且拿到了一个临时补丁验证了一部分场景。虽然最终出于稳妥考虑业务侧还是选择用转发对象方案但至少心里有底了不再瞎猜。不过也要提醒一句升级补丁通常需要停机窗口而且数据库升级对周边系统的影响面比较大一定要在测试环境充分回归后再上生产别为了一个同义词特性冒整体风险。4. 同义词相关高频坑位速查与实战排查技巧4.1 同义词相关高频坑位速查表以下是我在达梦上使用同义词时遇到或搜集到的高频问题整理成速查表方便大家直接对照排查。现象可能原因处理方式创建同义词时提示无效的对象名目标对象不存在或当前用户无访问权限确认对象是否存在、属主是否正确先给基对象授权创建同义词成功SELECT同义词报对象不存在表/视图同义词本身可用但权限没给到位给基对象授SELECT权限不要只授同义词权限存储过程同义词创建成功但CALL失败达梦当前版本对过程同义词解析支持不完整改用全限定名或转发对象自定义类型同义词声明变量失败达梦PL/SQL编译器不展开类型同义词声明时直接用底层类型名或用包封装同义词指向同义词达梦和Oracle都不支持链式同义词改成直接指向最终基对象公有同义词和私有同义词同名时结果异常名称解析优先私有再公有容易混淆统一规划命名尽量避免同名备份还原后同义词失效同义词指向的底层对象没跟着还原或属主变了还原后核对字典表映射关系小写、带引号创建的同义词查不到名称大小写敏感问题统一用大写不带引号的命名规范4.2 三条最实用的排查技巧排查同义词问题不需要什么花哨的工具掌握三个小技巧就够了。第一个技巧是“最小案例法”。遇到问题不要直接在业务大代码里调试先创建一个测试用户、一张测试表、一个测试存储过程把场景缩到最小一步步验证。我当时就是在测试用户里把表、视图、过程、类型各建了一套同义词逐个执行才快速锁定问题范围。这个方法看起来笨但效率极高。第二个技巧是“字典视图核对法”。通过查询达梦系统自带的同义词相关字典视图比如USER_SYNONYMS、ALL_SYNONYMS、DBA_SYNONYMS可以看到同义词的名称、属主、指向对象等信息。在排查“同义词是否建对”这个问题时这比任何工具都直观。注意不同版本的字典视图名称和字段可能略有差异但基本都遵循Oracle风格很好上手。第三个技巧是“执行计划验证法”。对于表、视图这类可以进行DML操作的同义词用一个简单的语句查看执行计划。执行计划如果正常展开成基表说明同义词解析成功如果报错或者计划里还保留着同义词名字说明解析链路有问题。这个技巧对判断“那条路径走通了”非常有用。4.3 从Oracle迁到达梦同义词专项检查清单最后我把这次项目里沉淀的同义词专项检查清单分享出来给大家在迁移项目里做个参考。盘点查询现有环境里所有同义词记录名称、类型、指向对象、属主分类按表/视图/序列、存储过程/函数/包、自定义类型分成三类验证对每类同义词做最小用例验证覆盖SELECT、INSERT、UPDATE、DELETE、CALL、声明变量等典型使用场景改造对不支持的场景按前文方案改造记录改造点和影响范围授权检查基对象的权限确保应用账号权限已经覆盖同义词背后的真实对象回归上线前做一轮同义词专项回归测试重点验证迁移后的存量脚本留档把同义词支持情况、版本号、验证结果整理成文档方便后续维护。做完这一套不敢说同义词问题完全绝迹但至少能让你在项目上线前就把雷排掉一大半。同义词这个特性在Oracle里是“润物细无声”的基础能力按说不需要特别关注。但在达梦上它偏偏成了一个需要专门踩坑、专项验证的对象。我个人在实际项目里的体会是凡是涉及国产数据库迁移千万不要默认“Oracle能干的达梦就一定能干”哪怕是一个小小的同义词也要先实测再大规模改造。最后再分享一个小技巧不管你是给表建同义词还是给过程建同义词建完之后第一件事不是继续写业务代码而是用一个最简短的语句把同义词“用一次”比如SELECT COUNT(*) FROM 同义词名;或者CALL 同义词名(...);能用再走下一步不能用马上换方案。这个习惯能帮你把问题消灭在最早期而不是等代码写了一堆再回头返工。