RPGLE动态SQL中SELECT子句使用?参数返回空值问题求助
SQLRPGLE动态SQL:参数占位符
?无法用于字段名等标识符的原因及解决办法 你遇到的问题是动态SQL里一个非常常见的误区:参数占位符?仅能用于传递数据值(比如WHERE条件里的筛选值),绝对不能用来替换标识符(像字段名、表名、列名这类数据库对象名称)。
为什么你的代码会返回空值?
当你执行SELECT ? FROM testpf WHERE fld01 = 3并传入'FLD02'作为参数时,数据库并不会把?解析成字段名FLD02,而是把它当作一个字符串常量来处理——也就是查询返回的是你传入的字符串'FLD02 '(因为fieldName01是10A类型,赋值后会补空格)。但由于你期望的是字段FLD02的实际数据,而实际得到的是字符串常量,再加上RPG变量的空格处理逻辑,最终显示为空。
更关键的是,这种用法从根本上违背了参数占位符的设计目的,它本来就不是用来替换数据库对象名称的。
正确的做法:动态拼接标识符到SQL语句中
如果需要动态指定字段名这类标识符,你必须直接把它们拼接进SQL字符串里,而不是用参数占位符。不过要注意SQL注入风险,如果这些标识符来自用户输入,一定要做严格的合法性校验(比如校验是否是表中存在的字段)。
修改你的第三段代码示例:
text = *blanks; fieldName01 = 'FLD02'; // 直接把字段名拼进SQL语句,用%trim去除多余空格 stm = 'SELECT ' + %trim(fieldName01) + ' FROM testpf WHERE fld01 = 3'; exec sql prepare s3 from :stm; exec sql declare c3 cursor for s3; exec sql open c3; exec sql fetch c3 into :text; exec sql close c3; dsply text; // 现在会正确打印'Text 03'
对于第四段既要动态字段名又要动态筛选字段的代码,我们可以拼接标识符,同时保留值的参数化(避免SQL注入):
text = *blanks; fieldName01 = 'FLD02'; fieldName02 = 'FLD01'; fieldName03 = '4'; // 拼接字段名,筛选值仍用参数占位符 stm = 'SELECT ' + %trim(fieldName01) + ' FROM testpf WHERE ' + %trim(fieldName02) + ' = ?'; exec sql prepare s4 from :stm; exec sql declare c4 cursor for s4; exec sql open c4 using :fieldName03; exec sql fetch c4 into :text; exec sql close c4; dsply text; // 现在会正确打印'Text 04'
补充说明
- 动态SQL的参数占位符
?设计目的是为了参数化查询,既可以避免SQL注入攻击,又能让数据库缓存执行计划提升性能,但它只能处理数据值,不能处理数据库对象名称。 - 拼接标识符时,一定要确保标识符的合法性(比如字段名确实存在于目标表中),如果是用户输入的标识符,必须严格校验,防止恶意注入。
附:你的原始代码及环境信息
DDS定义(TESTPF)
A************************************************************************** A* A*------------------------------------------------------------------------- A* A R TESTPFR A FLD01 2S 0 A FLD02 20A A**************************************************************************
测试数据
FLD01 FLD02 000001 1 Text 01 000002 2 Text 02 000003 3 Text 03 000004 4 Text 04 000005 5 Text 05 000006 6 Text 06 000007 7 Text 07 000008 8 Text 08 000009 9 Text 09 000010 10 Text 10
程序代码(TST001I)
D********************************************************************************************** D* Standalone Fields D*--------------------------------------------------------------------------------------------- D stm s 500a inz(*blanks) D fieldName01 s 10a inz(*blanks) D fieldName02 s 10a inz(*blanks) D fieldName03 s 2a inz(*blanks) D text s 20a inz(*blanks) C********************************************************************************************** C* M A I N P R O G R A M M C********************************************************************************************** stm = 'SELECT fld02 FROM testpf WHERE fld01 = 1'; exec sql prepare s1 from :stm; exec sql declare c1 cursor for s1; exec sql open c1; exec sql fetch c1 into :text; exec sql close c1; dsply text; // Prints 'Text 01' text = *blanks; stm = 'SELECT fld02 FROM testpf WHERE fld01 = ?'; exec sql prepare s2 from :stm; exec sql declare c2 cursor for s2; fieldName03 = '2'; exec sql open c2 using :fieldName03; exec sql fetch c2 into :text; exec sql close c2; dsply text; // Prints 'Text 02' text = *blanks; stm = 'SELECT ? FROM testpf WHERE fld01 = 3'; exec sql prepare s3 from :stm; exec sql declare c3 cursor for s3; fieldName01 = 'FLD02'; exec sql open c3 using :fieldName01; exec sql fetch c3 into :text; exec sql close c3; dsply text; // Prints ' ' text = *blanks; stm = 'SELECT ? FROM testpf WHERE ? = ?'; exec sql prepare s4 from :stm; exec sql declare c4 cursor for s4; fieldName01 = 'FLD02'; fieldName02 = 'FLD01'; fieldName03 = '4'; exec sql open c4 using :fieldName01, :fieldName02, :fieldName03; exec sql fetch c4 into :text; exec sql close c4; dsply text; // Prints ' ' text = *blanks; *inlr = *on; C**********************************************************************************************
程序输出
DSPLY Text 01 DSPLY Text 02 DSPLY DSPLY DSPLY
内容的提问来源于stack exchange,提问作者Radinator
相关产品推荐
相关产品推荐

