DB2 iSeries执行MAX(CASE WHEN)查询触发AccessViolationException异常
异常根本原因
你遇到的System.AccessViolationException属于IBM iSeries Access v12版本ODBC驱动的底层bug,并非上层C#业务代码逻辑错误。只有当前带CAST转FLOAT(53)的行转列聚合查询触发异常、其他查询正常的表现,也符合驱动在解析特定类型的聚合返回结果集时发生内存访问越界的特征。
补充说明:.NET Framework 4.0及以上版本默认不会捕获损坏状态异常(Corrupted State Exception),所以你写的普通
catch (Exception ex)无法捕获这个驱动层面抛出的异常,属于正常表现。
排查解决步骤
按优先级从高到低尝试以下方案:
验证SQL服务端合法性
把你的原始SQL直接放到AS400自带的原生查询工具(比如STRSQL、ACS SQL编辑器)中运行,如果服务端直接报错,先修正SQL语法/逻辑问题;如果服务端能正常返回结果,即可确认是客户端驱动的bug。修改SQL规避驱动bug(优先推荐,不需要改环境配置)
两个可单独或组合使用的修改方案:
- 去掉SQL层面的FLOAT类型转换,改为在C#代码中做类型转换,避开驱动解析FLOAT(53)结果的触发场景,修改后的SQL示例:
SELECT TABLENAME1.SLONO AS ORDER_NO, TABLENAME1.SLLNNO AS LINE, MAX(CASE WHEN TABLENAME2.CZVRNM in ('SLEEVEDEPTH', 'LENGTH') THEN RTRIM(TABLENAME2.CZREFD) END) AS SLEEVE, MAX(CASE WHEN TABLENAME2.CZVRNM in ('INSTALLATION') THEN RTRIM(TABLENAME2.CZREFD) END) AS DAMPER_AI FROM LOCATION.LOCATION2.TABLENAME1 TABLENAME1 LEFT JOIN LOCATION.LOCATION2.TABLENAME2 TABLENAME2 ON (TABLENAME1.SLONO = TABLENAME2.SPONO AND TABLENAME1.SLLNNO = TABLENAME2.SPLNNO) GROUP BY TABLENAME1.SLONO, TABLENAME1.SLLNNO
拿到DataTable结果后,自行遍历将SLEEVE、DAMPER_AI列的字符串值转换为double类型即可。
- 把原来的列别名
ORDER替换为非SQL保留字(比如上面示例的ORDER_NO),旧版驱动对保留字作为返回列名的处理逻辑存在缺陷,也可能触发内存异常。
- 修复驱动层面问题
- 升级驱动到最新的IBM i Access Client Solutions (ACS) 版本,该版本已经修复了大量v12旧版驱动的内存访问类bug;
- 如果暂时无法升级驱动,可尝试将32位驱动替换为同版本的64位驱动,或改用原生ODBC接口读取数据,绕开
IBM.Data.DB2.iSeries封装层的问题。
- 代码捕获兜底(不推荐,仅用于临时排查)
如果需要捕获该异常做日志记录,可在执行查询的方法上添加以下特性,让.NET允许捕获损坏状态异常:
[HandleProcessCorruptedStateExceptions] [SecurityCritical] public void YourQueryMethod() { // 原有查询逻辑 }
内容的提问来源于stack exchange,提问作者ArminAH
相关产品推荐
相关产品推荐

