VBA与Oracle SQL Developer调用PL/SQL函数输出结果不一致问题
我遇到一个非常特殊的问题:从VBA调用PL/SQL函数得到的输出,与在Oracle SQL Developer中执行同一函数的输出差异很大,请查看附件截图,第一张是VBA输出结果。
以下是我在ORACLE SQL Developer中得到的输出:
更特殊的是,该问题仅出现在部分FSYM_ID上,其余FSYM_ID的查询结果完全一致。
我已查阅过相关问题,有用户提到问题原因与hash joins有关,但我没有使用任何hash joins相关的语法。
我使用ADO和Range.CopyFromRecordSet方法将数据导入Excel,执行的查询语句完全一致,如下:
select FSYM_ID, CURRENT_FY, ROUND(VALUATION_PB_HIGH,0), ROUND(VALUATION_PB_LOW,0) from table(PRICE2BV('07BR8Q-R'))
我已更新SQL Developer的截图,两边的差异为特定年份的高低值,我已经在SQL Developer输出中做了高亮标注。
关于该Oracle PL/SQL函数的补充说明:
- 函数返回全局临时表(global temporary table)中的数据;
- 函数中使用的变量仅为number或varchar类型;
- 函数入参仅为公司标识符;
- 我已尝试直接通过表查询获取输出,仍然无法解决问题。
我还尝试过用GetRows方法获取数据,对Recordset观察后发现:从VBA查询得到的Recordset中的值,本身就和Oracle SQL Developer中的查询结果不同。
恳请各位帮忙排查该问题,多谢。
排查建议
你可以按以下优先级逐一验证:
- 会话级参数差异校验
两个客户端连接Oracle时的会话参数不一致是最常见的原因。分别在SQL Developer和VBA的数据库连接会话中执行以下查询,比对返回结果是否一致:
select * from nls_session_parameters;
重点关注NLS_NUMERIC_CHARACTERS、NLS_DATE_FORMAT、NLS_SORT这类会影响数值计算、排序逻辑的参数,不同的参数会直接导致ROUND函数输出、结果集排序的差异。
2. 全局临时表的会话隔离特性验证
你用到的全局临时表默认是会话级隔离的,如果PL/SQL函数内部有逻辑依赖之前会话产生的临时表数据,不同客户端的独立会话会读取到不同的数据。可以先修改PL/SQL函数逻辑,在返回结果前先把临时表的内容写入一张普通物理表做快照,分别用两个客户端执行查询后比对物理表中的快照数据,就能确认是函数计算逻辑的差异还是数据传输过程的差异。
3. ADO驱动版本与数据类型映射校验
检查VBA引用的Oracle驱动版本(是Oracle官方驱动还是微软的ODBC for Oracle驱动),低版本驱动存在数值类型精度丢失的问题。你可以把查询语句中的ROUND函数去掉,直接返回VALUATION_PB_HIGH、VALUATION_PB_LOW的原始值,比对两边的原始数值是否有精度差异,确认是否是驱动在解析NUMBER类型时自动做了截断。
4. 执行计划差异排查
即使你没有显式写hash joins语法,Oracle优化器也可能自动选择hash join作为执行计划。分别在两个会话中执行以下语句获取执行计划,比对执行计划是否一致:
explain plan for select FSYM_ID, CURRENT_FY, ROUND(VALUATION_PB_HIGH,0), ROUND(VALUATION_PB_LOW,0) from table(PRICE2BV('07BR8Q-R')); select * from table(dbms_xplan.display());
如果执行计划不同,可以通过加hint固定执行计划的方式验证是否是执行计划导致的结果差异。
内容的提问来源于stack exchange,提问作者Shekhar Nalawade

