DB2/400 中作为 SProc 一部分的动态 CTE
Dynamic CTE's as part of a SProc in DB2/400
我正在尝试在 V7R2 环境中 db2/400 中编写一个 SProc,它会根据传递的参数创建一个 CTE。然后我需要在 CTE 上执行递归查询。
我 运行 正在创建和执行动态 CTE。
根据http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzpreph2.htm
prepare
语句不能直接与 WITH
或 SELECT
语句一起使用。
我尝试将动态 CTE 和动态 SELECT
包装在 VALUES INTO
中,并成功准备了语句。当我尝试执行该语句时,问题就来了。
我得到一个错误代码SQL0518
,定义在这里(CTRL+F for 'SQL0518'跳转):http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/rzala/rzalamsg.html(注意*:这个link适用于 V5R2,但我的错误的错误代码和文本部分与此处列出的错误完全相同,代码相同。所以我确定错误代码在不同版本之间保持相同)
从列出的 3 条恢复建议来看,第二条似乎不太可能,因为我的 execute
是我的 prepare
之后的下一行。建议 3 似乎也不太可能,因为没有使用 commit
或 rollback
。所以我倾向于相信建议 1 适用于我的具体情况。但是,我不明白如何执行建议的步骤。
If &1 identifies a prepared SELECT or DECLARE PROCEDURE statement, a different prepared statement must be named in the EXECUTE statement.
我是否应该为同一个执行准备两个准备语句?这在句法上看起来如何?
这是我的 SProc 的代码供参考:
CREATE OR REPLACE PROCEDURE DLLIB/G_DPIVOT@ (
IN TABLE_NAME CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOT CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOTFLD CHAR(12) CCSID 37 DEFAULT '' ,
IN "VALUE" DECIMAL(10, 0) DEFAULT 0 ,
INOUT LIST CHAR(5000) CCSID 37 )
LANGUAGE SQL
SPECIFIC DLLIB/G_DPIVOT@
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
CONCURRENT ACCESS RESOLUTION DEFAULT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE STMT1 VARCHAR ( 1000 ) ;
SET STMT1 = 'WITH DETAILS ( ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' , CURR , PREV ) AS ( ' ||
'SELECT ' || TRIM ( PIVOT ) || ' ,' || TRIM ( PIVOTFLD ) || ',' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) AS CURR ,' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) - 1 AS PREV' ||
' FROM ' || TRIM ( TABLE_NAME ) ||
' WHERE ' || TRIM ( PIVOT ) || ' = ' || TRIM ( VALUE ) ||
' GROUP BY ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' )' ||
' VALUES( SELECT MAX ( TRIM ( L '','' FROM CAST ( SYS_CONNECT_BY_PATH ( ' || TRIM ( PIVOTFLD ) || ' , '','' ) AS CHAR ( 5000 ) ) ) )' ||
' FROM DETAILS ' ||
' START WITH CURR = 1 ' ||
' CONNECT BY NOCYCLE ' || TRIM ( PIVOT ) || ' = PRIOR ' || TRIM ( PIVOT ) || ' AND PREV = PRIOR CURR) INTO ?' ;
--SET LIST = STMT1; -- If I execute the value of LIST in interactive SQL everything is as expected (minus the VALUES INTO ofcourse)
PREPARE S1 FROM STMT1 ;
EXECUTE S1 USING LIST; -- If I comment this I don't get an error, but I also don't get a return value in LIST)
END ;
感谢任何帮助。
编辑 1:我正在尝试创建一个具有 5 个参数的 SProc(我将使用它来创建 UDF)。我正在尝试对跨越多个记录的单个字段进行透视,以便将值作为逗号分隔的字符串返回。不过我想让它动态化,这样我就可以 re-use 它适用于很多情况。一个示例调用是:CALL DLLIB.G_DPIVOT@(TABLE, PIVOT, PIVOTFLD, VALUE, LIST);
其中 TABLE
是我要转换的 table 的名称,PIVOT
是记录之间的共性 (FK),PIVOTFLD
是我想压缩成单个字符串的字段,VALUE
是我想用来转换的 FK 值,LIST
是包含结果字符串的 OUT 参数。您可以在此处阅读有关 non-dynamic 实施的更多信息:http://www.mcpressonline.com/sql/techtip-combining-multiple-row-values-into-a-single-row-with-sql-in-db2-for-i.html
当我有一个 header table 与另一个 table 有 one-to-many 关系时使用。然后,我将能够根据 PK/FK 关系汇总 "many" table 中特定字段的所有值。
编辑 2:
这是我最近的一次尝试,我认为我设法使用 EXECUTE IMMEDIATE
成功创建了 CTE,现在我正在尝试对其执行简单的 select。我正在尝试使用 DB2 游标,但在 DECLARE C2 CURSOR FOR S2;
行的 "C2" 处出错。我对 DB2 游标没有太多经验,但相信我正在以正确的方式使用它们。
DECLARE STMT1 VARCHAR ( 1000 ) ;
DECLARE STMT2 VARCHAR ( 1000 ) ;
SET STMT1 = 'WITH DETAILS ( ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' , CURR , PREV ) AS ( ' ||
'SELECT ' || TRIM ( PIVOT ) || ' ,' || TRIM ( PIVOTFLD ) || ',' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) AS CURR ,' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) - 1 AS PREV' ||
' FROM ' || TRIM ( TABLE_NAME ) ||
' WHERE ' || TRIM ( PIVOT ) || ' = ' || TRIM ( VALUE ) ||
' GROUP BY ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' )';
EXECUTE IMMEDIATE STMT1;
SET STMT2 = "SELECT * FROM DETAILS";
PREPARE S2 FROM STMT2;
DECLARE C2 CURSOR FOR S2;
OPEN C2;
FETCH C2 INTO LIST;
CLOSE C2;
有人发现这些更改有什么问题吗?
这是准确的错误消息(不包括建议文本):
SQL State: 42601
Vendor Code: -104
Message: [SQL0104] Token C2 was not valid. Valid tokens: GLOBAL.
编辑 3(最终存储过程):
@user2338816 提供所有帮助。参见他的 post 解决方案的解释,但这里是最终的 SProc 以供参考:
CREATE PROCEDURE DLLIB/G_DPIVOT@ (
IN TABLE_NAME CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOT CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOTFLD CHAR(12) CCSID 37 DEFAULT '' ,
IN "VALUE" DECIMAL(10, 0) DEFAULT 0 ,
INOUT LIST CHAR(5000) CCSID 37 )
LANGUAGE SQL
SPECIFIC DLLIB/G_DPIVOT@
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
CONCURRENT ACCESS RESOLUTION DEFAULT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE STMT1 VARCHAR ( 1000 ) ;
DECLARE C1 CURSOR FOR S1 ;
SET STMT1 = 'WITH DETAILS ( ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' , CURR , PREV ) AS ( ' ||
'SELECT ' || TRIM ( PIVOT ) || ' ,' || TRIM ( PIVOTFLD ) || ',' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) AS CURR ,' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) - 1 AS PREV' ||
' FROM ' || TRIM ( TABLE_NAME ) ||
' WHERE ' || TRIM ( PIVOT ) || ' = ' || TRIM ( VALUE ) ||
' GROUP BY ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' )' ||
' SELECT MAX ( TRIM ( L '','' FROM CAST ( SYS_CONNECT_BY_PATH ( ' || TRIM ( PIVOTFLD ) || ' , '','' ) AS CHAR ( 5000 ) ) ) ) ' ||
' FROM DETAILS ' ||
' START WITH CURR = 1 ' ||
' CONNECT BY NOCYCLE ' || TRIM ( PIVOT ) || ' = PRIOR ' || TRIM ( PIVOT ) || ' AND PREV = PRIOR CURR' ;
PREPARE S1 FROM STMT1 ;
OPEN C1 ;
FETCH C1 INTO LIST ;
CLOSE C1 ;
END ;
基本问题在 EXECUTE 中。你不能"execute"准备好的SELECT。相反,您需要为 S1 声明 CURSOR 并从 CURSOR 获取行。请注意,如果允许,'executing' 和 SELECT 语句实际上不会执行任何操作;它只是 "SELECT",所以 EXECUTE 没有多大意义。 (SELECT INTO 语句可以不同,但不清楚它在这里是否合适。)
打开一个 CURSOR 和 return 一个结果集而不是 FETCHing 行是可能的。随着对您实际想要如何使用它的更多定义,应该可以进行一些详细说明。
编辑:
第二题:
我已经为您的原始 CTE 和您编辑的问题中的 CTE 创建了更具可读性的版本。原文:
WITH DETAILS ( PIVOT , PIVOTFLD , CURR , PREV ) AS (
SELECT PIVOT , PIVOTFLD ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) AS CURR ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) - 1 AS PREV
FROM TABLE_NAME
WHERE PIVOT = VALUE
GROUP BY PIVOT , PIVOTFLD )
VALUES( SELECT MAX ( CAST ( SYS_CONNECT_BY_PATH ( PIVOTFLD , ',' ) AS CHAR ( 5000 ) ) ) )
FROM DETAILS
START WITH CURR = 1
CONNECT BY NOCYCLE PIVOT = PRIOR PIVOT AND PREV = PRIOR CURR) INTO ? ;
您在 CTE 之后有一个 VALUE INTO 语句。据我所知,那是无效的。
以及您编辑的示例:
WITH DETAILS ( PIVOT , PIVOTFLD , CURR , PREV ) AS (
SELECT PIVOT ,
PIVOTFLD ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) AS CURR ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) - 1 AS PREV
FROM TABLE_NAME
WHERE PIVOT = VALUE
GROUP BY PIVOT , PIVOTFLD );
好吧,它只是一个没有关联的 SELECT 引用它的裸 CTE。您稍后会尝试 PREPARE a SELECT 语句,但两者需要一起进行。您无法单独执行 CTE。
尝试将它们放在一起作为一个语句,看看是否有 CURSOR 在结果上创建。变量 STMT1 看起来像这样:
WITH DETAILS ( PIVOT , PIVOTFLD , CURR , PREV ) AS (
SELECT PIVOT ,
PIVOTFLD ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) AS CURR ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) - 1 AS PREV
FROM TABLE_NAME
WHERE PIVOT = VALUE
GROUP BY PIVOT , PIVOTFLD )
SELECT * FROM DETAILS ;
请注意,语句末尾包含 SELECT。 WITH ...
子句后跟 单个语句 中的 SELECT ...
,这是 PREPAREd。然后将在该语句上打开 CURSOR。
编辑 2:
我已经修改了我已经使用了一段时间的示例 CTE,以适应存储过程和 return 一个值。它是在我的 i 6.1 系统上编译和 运行 的。 CTE 是根据放入 VARCHAR 的字符串准备的,然后在其上打开 CURSOR。在 WHILE 循环中提取行。
CTE 生成汇总行,然后将这些行与来自 QIWS/QCUSTCDT 的详细信息行联合。摘要由 STATE 提供 BALDUE 的小计。 WHILE 循环有点毫无意义;它仅显示对行的提取和处理。唯一的操作是计算 CTE 中 不是 摘要行的行数。这与基本 table 中的行数基本相同。行数在 rowCnt OUT 参数中 returned。
源代码是 copy/pasted,但有两个来源。首先,在从已编译的存储过程生成 SQL 之后,CREATE PROCEDURE 语句取自 iNavigator 的 'Run SQL scripts' 实用程序。其次,BEGIN ... END 复合语句主体来自我输入到 iNavigator New-> Procedure 函数中的原始语句。虽然两者在逻辑上是等价的,但我想保留输入的实际行。您可以 copy/paste 将整个源代码放入 'Run SQL Scripts' 或通过实用程序创建过程,并且仅 copy/paste BEGIN ... END 复合语句在前两个选项卡中输入值后新建->程序函数。
我有一个名为 SQLEXAMPLE 的模式,我在其中构建了类似的东西。您需要调整架构和过程名称以适应您的环境。 QIWS/QCUSTCDT table 应该存在于几乎所有 AS/400-series 系统上。
CREATE PROCEDURE SQLEXAMPLE.CTE_CustCDT (
OUT rowCnt INTEGER )
LANGUAGE SQL
SPECIFIC SQLEXAMPLE.CTECUSTCDT
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
CLOSQLCSR = *ENDMOD ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE sumRows INTEGER DEFAULT 0 ;
DECLARE cusNum INTEGER ;
DECLARE lstNam CHAR(10) ;
DECLARE state CHAR(2) ;
DECLARE balDue DECIMAL(7, 2) ;
DECLARE stmt1 VARCHAR(512) ;
DECLARE at_end INT DEFAULT 0 ;
DECLARE not_found
CONDITION FOR '02000';
DECLARE c1 CURSOR FOR c1Stmt ;
DECLARE CONTINUE HANDLER FOR not_found
SET at_end = 1 ;
SET stmt1 = 'with t1 As(
SELECT 0 ,''Tot'' , state , sum( balDue )
FROM qiws.qcustcdt
GROUP BY state
ORDER BY state
)
select cusNum , lstNam , state, balDue
from qiws.qcustcdt
union
select *
from t1
order by state FOR FETCH ONLY' ;
PREPARE c1Stmt FROM stmt1 ;
OPEN c1 ;
FETCH C1 INTO cusNum , lstNam , state , balDue ;
WHILE at_end = 0 DO
IF cusNum <> 0 THEN SET sumRows = sumRows + 1 END IF ;
FETCH C1 INTO cusNum , lstNam , state , balDue ;
END WHILE ;
SET rowCnt = sumRows ;
CLOSE c1 ;
END
当 CTE 在 STRSQL 中单独为 运行 时,输出的前几行如下所示:
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+.
CUSNUM LSTNAM STATE BALDUE
475,938 Doe CA 250.00
0 Tot CA 250.00
389,572 Stevens CO 58.75
0 Tot CO 58.75
938,485 Johnson GA 3,987.50
0 Tot GA 3,987.50
846,283 Alison MN 10.00
583,990 Abraham MN 500.00
0 Tot MN 510.00
摘要行应该很容易识别。当从 'Run SQL Scripts' 调用存储过程时,结果输出为:
Connected to relational database TISI on Tisi as Toml - 090829/Quser/Qzdasoinit
> call SQLEXAMPLE.CTE_CustCDT( 0 )
Return Code = 0
Output Parameter #1 = 12
Statement ran successfully (570 ms)
该系统上的 QIWS/QCUSTCDT table 有 12 行,与值 returned 匹配。
它与您想要的 CTE 不完全相同,但它应该证明可以使用动态 CTE。它还展示了 FETCH 如何出于任何需要的目的从 CTE 中提取行。
我正在尝试在 V7R2 环境中 db2/400 中编写一个 SProc,它会根据传递的参数创建一个 CTE。然后我需要在 CTE 上执行递归查询。
我 运行 正在创建和执行动态 CTE。
根据http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzpreph2.htm
prepare
语句不能直接与 WITH
或 SELECT
语句一起使用。
我尝试将动态 CTE 和动态 SELECT
包装在 VALUES INTO
中,并成功准备了语句。当我尝试执行该语句时,问题就来了。
我得到一个错误代码SQL0518
,定义在这里(CTRL+F for 'SQL0518'跳转):http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/rzala/rzalamsg.html(注意*:这个link适用于 V5R2,但我的错误的错误代码和文本部分与此处列出的错误完全相同,代码相同。所以我确定错误代码在不同版本之间保持相同)
从列出的 3 条恢复建议来看,第二条似乎不太可能,因为我的 execute
是我的 prepare
之后的下一行。建议 3 似乎也不太可能,因为没有使用 commit
或 rollback
。所以我倾向于相信建议 1 适用于我的具体情况。但是,我不明白如何执行建议的步骤。
If &1 identifies a prepared SELECT or DECLARE PROCEDURE statement, a different prepared statement must be named in the EXECUTE statement.
我是否应该为同一个执行准备两个准备语句?这在句法上看起来如何?
这是我的 SProc 的代码供参考:
CREATE OR REPLACE PROCEDURE DLLIB/G_DPIVOT@ (
IN TABLE_NAME CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOT CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOTFLD CHAR(12) CCSID 37 DEFAULT '' ,
IN "VALUE" DECIMAL(10, 0) DEFAULT 0 ,
INOUT LIST CHAR(5000) CCSID 37 )
LANGUAGE SQL
SPECIFIC DLLIB/G_DPIVOT@
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
CONCURRENT ACCESS RESOLUTION DEFAULT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE STMT1 VARCHAR ( 1000 ) ;
SET STMT1 = 'WITH DETAILS ( ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' , CURR , PREV ) AS ( ' ||
'SELECT ' || TRIM ( PIVOT ) || ' ,' || TRIM ( PIVOTFLD ) || ',' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) AS CURR ,' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) - 1 AS PREV' ||
' FROM ' || TRIM ( TABLE_NAME ) ||
' WHERE ' || TRIM ( PIVOT ) || ' = ' || TRIM ( VALUE ) ||
' GROUP BY ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' )' ||
' VALUES( SELECT MAX ( TRIM ( L '','' FROM CAST ( SYS_CONNECT_BY_PATH ( ' || TRIM ( PIVOTFLD ) || ' , '','' ) AS CHAR ( 5000 ) ) ) )' ||
' FROM DETAILS ' ||
' START WITH CURR = 1 ' ||
' CONNECT BY NOCYCLE ' || TRIM ( PIVOT ) || ' = PRIOR ' || TRIM ( PIVOT ) || ' AND PREV = PRIOR CURR) INTO ?' ;
--SET LIST = STMT1; -- If I execute the value of LIST in interactive SQL everything is as expected (minus the VALUES INTO ofcourse)
PREPARE S1 FROM STMT1 ;
EXECUTE S1 USING LIST; -- If I comment this I don't get an error, but I also don't get a return value in LIST)
END ;
感谢任何帮助。
编辑 1:我正在尝试创建一个具有 5 个参数的 SProc(我将使用它来创建 UDF)。我正在尝试对跨越多个记录的单个字段进行透视,以便将值作为逗号分隔的字符串返回。不过我想让它动态化,这样我就可以 re-use 它适用于很多情况。一个示例调用是:CALL DLLIB.G_DPIVOT@(TABLE, PIVOT, PIVOTFLD, VALUE, LIST);
其中 TABLE
是我要转换的 table 的名称,PIVOT
是记录之间的共性 (FK),PIVOTFLD
是我想压缩成单个字符串的字段,VALUE
是我想用来转换的 FK 值,LIST
是包含结果字符串的 OUT 参数。您可以在此处阅读有关 non-dynamic 实施的更多信息:http://www.mcpressonline.com/sql/techtip-combining-multiple-row-values-into-a-single-row-with-sql-in-db2-for-i.html
当我有一个 header table 与另一个 table 有 one-to-many 关系时使用。然后,我将能够根据 PK/FK 关系汇总 "many" table 中特定字段的所有值。
编辑 2:
这是我最近的一次尝试,我认为我设法使用 EXECUTE IMMEDIATE
成功创建了 CTE,现在我正在尝试对其执行简单的 select。我正在尝试使用 DB2 游标,但在 DECLARE C2 CURSOR FOR S2;
行的 "C2" 处出错。我对 DB2 游标没有太多经验,但相信我正在以正确的方式使用它们。
DECLARE STMT1 VARCHAR ( 1000 ) ;
DECLARE STMT2 VARCHAR ( 1000 ) ;
SET STMT1 = 'WITH DETAILS ( ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' , CURR , PREV ) AS ( ' ||
'SELECT ' || TRIM ( PIVOT ) || ' ,' || TRIM ( PIVOTFLD ) || ',' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) AS CURR ,' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) - 1 AS PREV' ||
' FROM ' || TRIM ( TABLE_NAME ) ||
' WHERE ' || TRIM ( PIVOT ) || ' = ' || TRIM ( VALUE ) ||
' GROUP BY ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' )';
EXECUTE IMMEDIATE STMT1;
SET STMT2 = "SELECT * FROM DETAILS";
PREPARE S2 FROM STMT2;
DECLARE C2 CURSOR FOR S2;
OPEN C2;
FETCH C2 INTO LIST;
CLOSE C2;
有人发现这些更改有什么问题吗?
这是准确的错误消息(不包括建议文本):
SQL State: 42601
Vendor Code: -104
Message: [SQL0104] Token C2 was not valid. Valid tokens: GLOBAL.
编辑 3(最终存储过程): @user2338816 提供所有帮助。参见他的 post 解决方案的解释,但这里是最终的 SProc 以供参考:
CREATE PROCEDURE DLLIB/G_DPIVOT@ (
IN TABLE_NAME CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOT CHAR(12) CCSID 37 DEFAULT '' ,
IN PIVOTFLD CHAR(12) CCSID 37 DEFAULT '' ,
IN "VALUE" DECIMAL(10, 0) DEFAULT 0 ,
INOUT LIST CHAR(5000) CCSID 37 )
LANGUAGE SQL
SPECIFIC DLLIB/G_DPIVOT@
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
CONCURRENT ACCESS RESOLUTION DEFAULT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE STMT1 VARCHAR ( 1000 ) ;
DECLARE C1 CURSOR FOR S1 ;
SET STMT1 = 'WITH DETAILS ( ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' , CURR , PREV ) AS ( ' ||
'SELECT ' || TRIM ( PIVOT ) || ' ,' || TRIM ( PIVOTFLD ) || ',' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) AS CURR ,' ||
' ROW_NUMBER ( ) OVER ( PARTITION BY ' || TRIM ( PIVOT ) || ' ORDER BY ' || TRIM ( PIVOTFLD ) || ' ) - 1 AS PREV' ||
' FROM ' || TRIM ( TABLE_NAME ) ||
' WHERE ' || TRIM ( PIVOT ) || ' = ' || TRIM ( VALUE ) ||
' GROUP BY ' || TRIM ( PIVOT ) || ' , ' || TRIM ( PIVOTFLD ) || ' )' ||
' SELECT MAX ( TRIM ( L '','' FROM CAST ( SYS_CONNECT_BY_PATH ( ' || TRIM ( PIVOTFLD ) || ' , '','' ) AS CHAR ( 5000 ) ) ) ) ' ||
' FROM DETAILS ' ||
' START WITH CURR = 1 ' ||
' CONNECT BY NOCYCLE ' || TRIM ( PIVOT ) || ' = PRIOR ' || TRIM ( PIVOT ) || ' AND PREV = PRIOR CURR' ;
PREPARE S1 FROM STMT1 ;
OPEN C1 ;
FETCH C1 INTO LIST ;
CLOSE C1 ;
END ;
基本问题在 EXECUTE 中。你不能"execute"准备好的SELECT。相反,您需要为 S1 声明 CURSOR 并从 CURSOR 获取行。请注意,如果允许,'executing' 和 SELECT 语句实际上不会执行任何操作;它只是 "SELECT",所以 EXECUTE 没有多大意义。 (SELECT INTO 语句可以不同,但不清楚它在这里是否合适。)
打开一个 CURSOR 和 return 一个结果集而不是 FETCHing 行是可能的。随着对您实际想要如何使用它的更多定义,应该可以进行一些详细说明。
编辑:
第二题:
我已经为您的原始 CTE 和您编辑的问题中的 CTE 创建了更具可读性的版本。原文:
WITH DETAILS ( PIVOT , PIVOTFLD , CURR , PREV ) AS (
SELECT PIVOT , PIVOTFLD ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) AS CURR ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) - 1 AS PREV
FROM TABLE_NAME
WHERE PIVOT = VALUE
GROUP BY PIVOT , PIVOTFLD )
VALUES( SELECT MAX ( CAST ( SYS_CONNECT_BY_PATH ( PIVOTFLD , ',' ) AS CHAR ( 5000 ) ) ) )
FROM DETAILS
START WITH CURR = 1
CONNECT BY NOCYCLE PIVOT = PRIOR PIVOT AND PREV = PRIOR CURR) INTO ? ;
您在 CTE 之后有一个 VALUE INTO 语句。据我所知,那是无效的。
以及您编辑的示例:
WITH DETAILS ( PIVOT , PIVOTFLD , CURR , PREV ) AS (
SELECT PIVOT ,
PIVOTFLD ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) AS CURR ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) - 1 AS PREV
FROM TABLE_NAME
WHERE PIVOT = VALUE
GROUP BY PIVOT , PIVOTFLD );
好吧,它只是一个没有关联的 SELECT 引用它的裸 CTE。您稍后会尝试 PREPARE a SELECT 语句,但两者需要一起进行。您无法单独执行 CTE。
尝试将它们放在一起作为一个语句,看看是否有 CURSOR 在结果上创建。变量 STMT1 看起来像这样:
WITH DETAILS ( PIVOT , PIVOTFLD , CURR , PREV ) AS (
SELECT PIVOT ,
PIVOTFLD ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) AS CURR ,
ROW_NUMBER ( ) OVER ( PARTITION BY PIVOT ORDER BY PIVOTFLD ) - 1 AS PREV
FROM TABLE_NAME
WHERE PIVOT = VALUE
GROUP BY PIVOT , PIVOTFLD )
SELECT * FROM DETAILS ;
请注意,语句末尾包含 SELECT。 WITH ...
子句后跟 单个语句 中的 SELECT ...
,这是 PREPAREd。然后将在该语句上打开 CURSOR。
编辑 2:
我已经修改了我已经使用了一段时间的示例 CTE,以适应存储过程和 return 一个值。它是在我的 i 6.1 系统上编译和 运行 的。 CTE 是根据放入 VARCHAR 的字符串准备的,然后在其上打开 CURSOR。在 WHILE 循环中提取行。
CTE 生成汇总行,然后将这些行与来自 QIWS/QCUSTCDT 的详细信息行联合。摘要由 STATE 提供 BALDUE 的小计。 WHILE 循环有点毫无意义;它仅显示对行的提取和处理。唯一的操作是计算 CTE 中 不是 摘要行的行数。这与基本 table 中的行数基本相同。行数在 rowCnt OUT 参数中 returned。
源代码是 copy/pasted,但有两个来源。首先,在从已编译的存储过程生成 SQL 之后,CREATE PROCEDURE 语句取自 iNavigator 的 'Run SQL scripts' 实用程序。其次,BEGIN ... END 复合语句主体来自我输入到 iNavigator New-> Procedure 函数中的原始语句。虽然两者在逻辑上是等价的,但我想保留输入的实际行。您可以 copy/paste 将整个源代码放入 'Run SQL Scripts' 或通过实用程序创建过程,并且仅 copy/paste BEGIN ... END 复合语句在前两个选项卡中输入值后新建->程序函数。
我有一个名为 SQLEXAMPLE 的模式,我在其中构建了类似的东西。您需要调整架构和过程名称以适应您的环境。 QIWS/QCUSTCDT table 应该存在于几乎所有 AS/400-series 系统上。
CREATE PROCEDURE SQLEXAMPLE.CTE_CustCDT (
OUT rowCnt INTEGER )
LANGUAGE SQL
SPECIFIC SQLEXAMPLE.CTECUSTCDT
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
CLOSQLCSR = *ENDMOD ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE sumRows INTEGER DEFAULT 0 ;
DECLARE cusNum INTEGER ;
DECLARE lstNam CHAR(10) ;
DECLARE state CHAR(2) ;
DECLARE balDue DECIMAL(7, 2) ;
DECLARE stmt1 VARCHAR(512) ;
DECLARE at_end INT DEFAULT 0 ;
DECLARE not_found
CONDITION FOR '02000';
DECLARE c1 CURSOR FOR c1Stmt ;
DECLARE CONTINUE HANDLER FOR not_found
SET at_end = 1 ;
SET stmt1 = 'with t1 As(
SELECT 0 ,''Tot'' , state , sum( balDue )
FROM qiws.qcustcdt
GROUP BY state
ORDER BY state
)
select cusNum , lstNam , state, balDue
from qiws.qcustcdt
union
select *
from t1
order by state FOR FETCH ONLY' ;
PREPARE c1Stmt FROM stmt1 ;
OPEN c1 ;
FETCH C1 INTO cusNum , lstNam , state , balDue ;
WHILE at_end = 0 DO
IF cusNum <> 0 THEN SET sumRows = sumRows + 1 END IF ;
FETCH C1 INTO cusNum , lstNam , state , balDue ;
END WHILE ;
SET rowCnt = sumRows ;
CLOSE c1 ;
END
当 CTE 在 STRSQL 中单独为 运行 时,输出的前几行如下所示:
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+.
CUSNUM LSTNAM STATE BALDUE
475,938 Doe CA 250.00
0 Tot CA 250.00
389,572 Stevens CO 58.75
0 Tot CO 58.75
938,485 Johnson GA 3,987.50
0 Tot GA 3,987.50
846,283 Alison MN 10.00
583,990 Abraham MN 500.00
0 Tot MN 510.00
摘要行应该很容易识别。当从 'Run SQL Scripts' 调用存储过程时,结果输出为:
Connected to relational database TISI on Tisi as Toml - 090829/Quser/Qzdasoinit
> call SQLEXAMPLE.CTE_CustCDT( 0 )
Return Code = 0
Output Parameter #1 = 12
Statement ran successfully (570 ms)
该系统上的 QIWS/QCUSTCDT table 有 12 行,与值 returned 匹配。
它与您想要的 CTE 不完全相同,但它应该证明可以使用动态 CTE。它还展示了 FETCH 如何出于任何需要的目的从 CTE 中提取行。