【问题标题】:DB2/400 Alternative to Opening a CursorDB2/400 打开游标的替代方案
【发布时间】:2016-01-07 17:50:17
【问题描述】:

我正在运行 DB2 for i,V7R2 TR3。

有人告诉我打开游标会带来很多开销,应尽可能避免。根据我的阅读,使用 EXECUTE INTO var1 USING var2 是一种替代方法,但我无法让它工作。我收到了SQL0104 error

这是我的存储过程:

BEGIN 
DECLARE STMT1 VARCHAR ( 500 ) ;     

SET STMT1 =     'SELECT SUBSTR (''' || TRIM(ITEM) || ''' , ( LENGTH ( TRIM ( PREFIX ) ) + 1 ) , ( 20 - LENGTH ( TRIM ( PREFIX ) ) ) ) ' ||
                'FROM   MYLIB.MYTABLE ' ||
                'WHERE  PREFIX = SUBSTR(''' || TRIM(ITEM) || ''', 0,LENGTH ( TRIM ( PREFIX ) ) + 1 ) ' ||
                'AND    SEQ1 = ' || TYPE || ' ' ||
                'ORDER BY   LENGTH ( TRIM ( PREFIX) ) DESC , TRIM (PREFIX) DESC ' ||
                'FETCH FIRST 1 ROWS ONLY ';

PREPARE S1 FROM STMT1; 
EXECUTE S1 INTO BASEITEM;

--OPEN C1 ; 
--  FETCH C1 INTO BASEITEM ; 
--CLOSE C1 ; 

IF(TRIM(BASEITEM) = '') THEN
    SET BASEITEM = ITEM;
END IF;
END  ; 

它定义了三个变量:

IN ITEM CHAR(20) CCSID 37 DEFAULT  ''  , 
IN TYPE INT DEFAULT  1  , 
INOUT BASEITEM CHAR(20) DEFAULT  '' 

当我有 EXECUTE INTO... 时,它不会编译,如果我使用 EXECUTE USING... 它会编译,但 BASEITEM 最终为空白,IF 语句解析为 true。

我已尝试遵循EXECUTE 文档,显然INTO 只能用于CALLVALUES INTO 语句。所以我随后尝试关注VALUES INTO 文档,但无法弄清楚如何使用它进行查询。

注意*:我想最终将连接变量更改为使用参数标记,但接下来我会担心这个。

我想我应该发布正在使用的表格: MYLIB.MYTABLE

Column |Prefix|SEQ1|SEQ2|
Row 1  |aaa   |   1|   3|
Row 2  |aab   |   1|   3|
Row 3  |aabd  |   2|   4|

我实际上是在传递一个字符串,然后从字符串中删除最长的前缀。然后我返回了新字符串。 SEQ1 只是前缀的类型(1 或 2),SEQ2 是前缀的长度。

【问题讨论】:

    标签: db2 ibm-midrange db2-400


    【解决方案1】:

    您收到错误是因为 EXECUTE stmt INTO 没有按照您认为的方式工作。

    真正的问题是您在不需要时尝试使用动态 SQL。请改用静态。

    BEGIN 
    
      SELECT SUBSTR (TRIM(ITEM), ( LENGTH ( TRIM ( PREFIX ) ) + 1 ) , ( 20 - LENGTH ( TRIM ( PREFIX ) ) ) )
        INTO BASEITEM
        FROM   MYLIB.MYTABLE 
       WHERE  PREFIX = SUBSTR(TRIM(ITEM), 0,LENGTH ( TRIM ( PREFIX ) ) + 1 )
              AND    SEQ1 = TYPE
       ORDER BY   LENGTH ( TRIM ( PREFIX) ) DESC , TRIM (PREFIX) DESC
       FETCH FIRST 1 ROWS ONLY;
    
    IF(TRIM(BASEITEM) = '') THEN
        SET BASEITEM = ITEM;
    END IF;
    END  ;
    

    【讨论】:

    • 我希望我可以标记多个答案,因为这将是其中之一,因为它非常有帮助,@mustaccio 刚刚回答了实际问题。不按照您的建议使用动态 sql 可以显着提高速度。我最初开始使用动态 SQL 是因为我试图使用 LIKE 子句将字符串与前缀进行比较,并且必须与 % 符号连接。当我放弃它时,我从未意识到将其转换回来会提高性能。
    • @d.lanza38,为了将来参考,您应该能够在静态语句中使用LIKE。唯一必须使用动态的地方是表名更改时。动态可以简化复杂的WHEREORDER BY...但如果您在WHEREORDER BY 中选择动态SQL 而不是CASE 语句,请确保使用参数标记'WHERE mycol = ?' 与直接连接用户输入进入声明。
    【解决方案2】:

    EXECUTE 只能用于 DML 语句,不能用于查询,因此除了声明动态游标之外,确实没有其他选择。话虽如此,没有“打开游标的开销”,与以任何其他方式执行查询一样,因为对于任何查询,游标仍将被隐式创建和打开,即使不是显式的。

    游标比基于集合的操作效率低,例如什么时候,而不是做类似INSERT INTO table1 SELECT something FROM table2 WHERE... 的事情,而是在table2 上打开一个游标,并在循环中一一插入行到table1。在这种情况下,效率低下的原因是执行单行插入(或更新)与多行更新,而不是游标本身,这与说“打开游标会带来很多开销”完全不同。

    由于在您的情况下无论如何您只获取一条记录,因此此循环与设置参数不适用。

    【讨论】:

    • 好的,谢谢您解决这个问题。它基本上归结为 N + 1 问题,并使用某些方法执行更少的查询以实现给定的目标。
    • 说得好。但让我指出,动态 SQL 与静态 SQL 相比额外开销。除了像@d.lanza38 这样没有参数标记的动态语句外,还容易受到SQL 注入攻击。在这种情况下,甚至不需要动态 SQL。
    • @Charles 问题是关于一般游标的开销(与什么相比,我不知道),而不是静态与动态语句。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-23
    • 1970-01-01
    相关资源
    最近更新 更多