ORA-00900:Oracle过程的SQL语句无效

时间:2016-12-27 23:51:06

标签: oracle stored-procedures plsql cursor dynamic-sql

我正在尝试执行以下程序,但一直收到错误(ORA-00900:无效的SQL语句)

CREATE OR REPLACE PROCEDURE RESETUSERSESSION (run IN VARCHAR2)
IS    
    cursor usersessiondetail_cur IS    
          SELECT usd.CLIENTID,usd.OPERID,usd.REGISTER,usd.MACHINE_ID,usd.SESSIONNUMBER
          FROM cashiering_dev.CSH_USER usr, cashiering_dev.CSH_USERSESSIONDETAIL usd
          WHERE usr.clientid = usd.clientid 
          AND usr.operid = usd.operid
          AND usr.register = usd.register
          AND usr.machine_id = usd.machine_id 
          AND usr.sessionnumber = usd.sessionnumber
          AND usr.Machine_ID = 'basrytest'
          AND usd.LOGOFFDATETIME IS NULL;


 BEGIN
      OPEN usersessiondetail_cur;      

        FOR vItems in usersessiondetail_cur
        LOOP
           EXECUTE IMMEDIATE 'UPDATE csh_UserSessionDetail 
                         SET ClientID =vItems.CLIENTID 
                        WHERE ClientID =vItems.CLIENTID 
                         AND OperID =vItems.OPERID 
                         AND Register =vItems.REGISTER 
                         AND Machine_ID =vItems.MACHINE_ID 
                         AND SessionNumber =vItems.SESSIONNUMBER';                     
       END LOOP;

      CLOSE usersessiondetail_cur;



END;

2 个答案:

答案 0 :(得分:1)

您的SQL无效,因为在执行动态SQL字符串时,游标投影名称不在范围内。您需要使用这样的占位符:

   FOR vItems in usersessiondetail_cur
    LOOP
       EXECUTE IMMEDIATE 'UPDATE csh_UserSessionDetail 
                     SET ClientID = :p1
                    WHERE ClientID = :p2 
                     AND OperID = :p3
                     AND Register = :p4 
                     AND Machine_ID = :p5 
                     AND SessionNumber = :p6' 
           using vItems.CLIENTID 
                 , vItems.CLIENTID 
                , vItems.OPERID 
                , Items.REGISTER 
                , vItems.MACHINE_ID 
                , vItems.SESSIONNUMBER;                     
   END LOOP;

您的动态代码不是匿名PL / SQL块或CALL语句,因此参数按位置而不是名称传递,因此您需要传递vItems.CLIENTID两次。 Find out more

其他观察

  • 首先,完全没有必要为此SQL实现动态执行。
  • OPEN和CLOSE游标语句不与FOR游标循环一起使用。
  • 此查询不需要显式游标声明。
  • 与基于set的UPDATE语句相比,带有游标循环的逐行激活行UPDATE是不好的做法,并且效率低下。
  • 您的程序未使用run参数...
  • ...但是光标确实有一个用于MACHINE_ID的硬编码字符串。
  • 最后,UPDATE语句实际上并不会更改表的状态,因为它设置了CLIENT_ID = CLIENT_ID,因此整个过程毫无意义。

除此之外,一切都很好。

我假设您将此作为测试来理解如何使用动态SQL而不是作为业务逻辑的实现。但即使它是一个测试,最好编写一段适当的代码来做某事。特别是当您在StackOverflow上与其他人共享代码时。发布包含如此多问题的代码会分散注意力,因为潜在的受访者不知道应该解决哪些问题。

答案 1 :(得分:0)

只有FOR循环的简单方法。在这种情况下,我们不需要打开关闭游标,因为这是由Oracle内部处理的。另外,我不明白是否需要再次更新客户端ID。如果我们在子句中选择客户端ID,则没有更新的重要性。无论如何享受:)

CREATE OR REPLACE
PROCEDURE RESETUSERSESSION(
    run IN VARCHAR2)
AS
BEGIN
  FOR vItems IN
  (SELECT usd.CLIENTID,
    usd.OPERID,
    usd.REGISTER,
    usd.MACHINE_ID,
    usd.SESSIONNUMBER
  FROM cashiering_dev.CSH_USER usr,
    cashiering_dev.CSH_USERSESSIONDETAIL usd
  WHERE usr.clientid      = usd.clientid
  AND usr.operid          = usd.operid
  AND usr.register        = usd.register
  AND usr.machine_id      = usd.machine_id
  AND usr.sessionnumber   = usd.sessionnumber
  AND usr.machine_id      = 'basrytest'
  AND usd.LOGOFFDATETIME IS NULL
  )
  LOOP
    UPDATE csh_UserSessionDetail
    SET ClientID      =vItems.CLIENTID
    WHERE ClientID    =vItems.CLIENTID
    AND OperID        =vItems.OPERID
    AND Register      =vItems.REGISTER
    AND Machine_ID    =vItems.MACHINE_ID
    AND SessionNumber =vItems.SESSIONNUMBER;
  END LOOP;
END;
/