问题
我有一个SQLRPGLE程序,它执行如下查询:
SELECT orapdt, oraptm, orodr#, c.ccctls, orbill, b.cuslmn, b.cusvrp, orocty, orost, o.cubzip, o.cucnty, ordcty, ordst, d.cubzip, d.cucnty
FROM order
LEFT JOIN cmtctlf c ON orbill = c.cccode
LEFT JOIN custmast b ON orbill = b.cucode
LEFT JOIN custmast o ON orldat = o.cucode
LEFT JOIN custmast d ON orcons = d.cucode
WHERE
orstat != 'C' AND
orbill IN ('ABCDE', 'VWXYZ', 'JKFRTE') AND
orapdt BETWEEN 2012365 AND 2013362 AND
o.cucnty = 'USA' AND
(o.cubzip LIKE '760%' OR o.cubzip LIKE '761%' OR o.cubzip LIKE '762%') AND
d.cubzip = '38652' AND
ordcty = 'NA' AND
ordst = 'MS' AND
d.cucnty = 'USA'
ORDER BY orapdt, oraptm, orodr#
字段定义:
orapdt 7 0
oraptm 4a
orodr# 7a
c.ccctls 6a
orbill 6a
b.cuslmn 2a
b.cusvrp 3a
orocty 4a
orost 2a
o.cubzip 5a
o.cucnty 3a
ordcty 4a
ordst 2a
d.cubzip 5a
d.cucnty 3a
c.cccode 6a
b.cucode 6a
o.cucode 6a
d.cucode 6a
我在作业日志中看到以下错误:
Field HVR0001 and value 1 not compatible. Reason 7.
Conversion error on host variable or parameter *N.
当我提示额外的消息信息时,我被告知:
The attributes of variable field HVR0001 in query record format FORMAT0001 are not compatible with the attributes of value number 1. The value is *N. The reason code is 7.
7 -- Value contains numeric data that is not valid
和
Host variable or parameter *N or entry 1 in a descriptor area contains a value that cannot be converted to the attributes required by the statement. Error type 6 occurred.
6 -- Numeric data that is not valid.
这些错误是由打开游标触发的:
...
exec sql PREPARE S1 FROM :sql_stmt;
exec sql DECLARE C1 SCROLL CURSOR FOR S1;
exec sql OPEN C1;
...
我的输出中也有QSQSVCDMP文件,其中填充了转储信息。我在里面看到的唯一有用的东西是对CPF4278和CPD4374的引用
CPF4278表示Query definition template &1 not valid.
CPD4374表示Field &1 and value &3 not compatible. Reason &5.
不幸的是,错误信息本身并不存在,只有字符串"CPF4278"one_answers"CPD4374"。
在程序中,我监视SQL错误代码,它们都是相同的:
SQLSTATE: 22023
SQLCODE: -302
SQLERRMC: <non-displayable character>*N
错误状态/代码表示"参数或变量值无效"。
What I've try…
在谷歌上搜索了很多之后,我试着:
- 删除ORDER BY子句(在OPEN上,数据被获取和ORDER BY子句)
- 将所有LEFT JOIN更改为INNER JOIN(这样做是为了确保没有NULL)在结果记录右侧)
- 在WHERE子句中添加" AND orapdt IS NOT NULL"
- 我忘记了更多的事情
我问的是…
我如何发现哪个字段中有坏数据?我知道HVR0001
是无效的,但是哪个字段是由HVR0001
表示的?我尝试以不同的顺序选择字段,但它总是HVR0001
有一个无效的值。
理想情况下,我希望能够打印出所有HVR*字段/值,以便我可以检查它们。
当我查看编译清单时,没有列出HVR*字段。这里列出了一些SQL_*字段,我可以看到SQL_00011
用于临时保存放入orapdt
中的数据。SQL_00011
的定义与orapdt
(7,0打包)完全相同。这是我的查询中唯一的数字字段…
我觉得我的问题是由如何连接文件引起的,不知何故,一个无效的值(可能是NULL)被放入我的orapdt
字段。
我也认为我的问题与执行许多这些查询一个接一个(一些具体的地方改变每个查询),因为我可以采取一个失败的查询,并把它放在它自己的程序和运行它,它工作得很好。
这是在DB2 for i (V6R1)上,所有涉及的文件都是使用DDS
创建的。编辑:下面是LIKE语句所需的主机变量(数据结构)和两个外部数据结构:
d eds_custmast e ds extname('CUSTMAST') inz
d eds_order e ds extname('ORDER') inz
d o ds
d orapdt like(ORAPDT)
d oraptm like(ORAPTM)
d orodr# like(ORODR#)
d orctls like(CUCODE)
d orbill like(ORBILL)
d orslmn like(CUSLMN)
d orcsr like(CUSVRP)
d orocty like(OROCTY)
d orost like(OROST)
d orozip like(CUBZIP)
d orocntry like(CUCNTY)
d ordcty like(ORDCTY)
d ordst like(ORDST)
d ordzip like(CUBZIP)
d ordcntry like(CUCNTY)
// Define an array to indicate nulls...
d o1nv s 3i 0 dim(15)
这里是获取数据的fetch语句
dow sqlcode = *zeros;
exec sql FETCH NEXT FROM C1 INTO :o :o1nv;
if sqlcode = *zeros;
// process the data.
endif;
enddo;
exec sql CLOSE C1;
我之前没有包括这个,只是因为错误发生在我打开游标时,而不是抓取一行。OPEN语句不应该知道任何关于o
数据结构的信息。
至于WHERE子句中发生了什么变化——所有这些都是动态构建的(因此可以更改),除了:
orstat != 'C' AND orapdt BETWEEN 2012365 AND 2013362
要找出实际错误是什么并不容易。我倾向于将这样的语句复制到IBM I Navigator中,并使用Visual Explain来尝试掌握优化器正在做出的决策。另一种方法是执行STRDBG并查看作业日志。当STRDBG生效时,优化器将信息消息放入作业日志中。但即使这样,也很难弄清楚。
在本例中,只有一个数字列orapdt
。尝试不使用该列的查询,看看这是否是罪魁祸首。
因为ORAPDT是您唯一的数字列,所以问题一定在那里。
问题在于DDS定义的文件的工作方式。当写入DDS定义的文件时,不检查值的有效性,因此看起来您在一个或多个记录上的ORAPDT中有非数字数据。SQL不喜欢这样,并抛出一个错误。
SQL (DDL)定义的表在写入值之前验证值,从而更好地保护数据库的完整性。
要解决您的问题,找到违规记录并修复或删除它们。
假设错误来自orapdt,您可以通过创建新变量或用其他数字替换null或垃圾值来监视它,例如null = 9999999,非数字= 8888888
SELECT case when orapdt is null
then 9999999
when TRANSLATE(SUBSTR(orapdt,1,LENGTH(orapdt)-1),' ','0123456789',' ') <>' '
then 8888888
else orapdt
end
, oraptm,
或通过STRSQL检查或运行SQL脚本检查违规记录
SELECT orapdt, oraptm, orodr#,
...
WHERE ( orapdt is null or TRANSLATE(SUBSTR(orapdt,1,LENGTH(orapdt)-1),' ','0123456789',' ') <>' ' ) AND
orstat != 'C' AND
......
怎么回事?
我在我的问题中发布的代码是在程序A中。程序A调用(通过CALLP)程序b。
程序A使用嵌入式SQL声明一个名为S1
的准备语句和一个名为C1
的可滚动游标。程序B还碰巧声明了一个名为S1
的预置语句和一个名为C1
的可滚动游标。
所发生的似乎是游标的相互干扰,因为它们具有相同的名称。我的信念是,在程序B中执行的查询正在获取对自身有效的数据-但对程序A中定义的查询无效。因此,当程序A滚动查询结果并调用程序B时,程序B执行的查询试图将无效值放入与程序A相关的字段中-这只发生在两个程序中的游标名称相同时。
我所做的就是给两个程序中的游标一个唯一的名字(例如PGMA_C1
和PGMB_C1
),错误就不再发生了。除了游标名称之外,没有其他更改。这违背了我在这里找到的信息(http://pic.dhe.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/rzala/rzalaccl.htm)
"游标的作用域:游标-name的作用域是定义它的源程序;也就是说,提交给预编译器的程序。因此,游标只能由使用游标声明预编译的语句引用。例如,从另一个单独编译的程序调用的程序不能使用由调用程序打开的游标。当然,这句话似乎与下面这句相矛盾:
游标只能在程序的同一实例中被引用除非CLOSQLCSR(*ENDJOB)、CLOSQLCSR(*ENDSQL)或CLOSQLCSR(*ENDACTGRP)在CRTSQLxxx命令中指定。
- 如果指定了CLOSQLCSR(*ENDJOB),游标可以被程序堆栈上的任何程序实例引用。
- 如果指定了CLOSQLCSR(*ENDSQL),游标可以被程序堆栈上的任何程序实例引用,直到最后一个SQL程序在程序栈的末端。
- 如果指定了CLOSQLCSR(*ENDACTGRP),则该游标可以被激活组中该模块的所有实例引用,直到激活组结束。
但是在我们的例子中,程序A和程序B都有
不幸的是,我没有时间深入研究这个问题。我已经确认,只要给每个程序一个唯一的游标名称就可以解决问题。CLOSQLCSR(*ENDMOD)
——所以两个游标不应该相互感知。在我发现使用唯一游标名称可以解决问题之前,我对所有数据进行了全面的测试。这两个程序使用的每个文件中的每个记录中的每个字段都包含有效数据。根据错误信息,我期望在某个地方有一个NULL或其他一些无效字符,但事实并非如此。
我很感谢你的回复和建议,+1左右:-)