游标内部程序有问题

时间:2012-10-09 13:33:02

标签: stored-procedures plsql oracle10g

CREATE TABLE TEST_CASE
  (
    ID NUMBER(19,2),
    CURRENCY_TYPE VARCHAR2(30),
    PAIDAMT NUMBER(19,2),
    RECVDAMT NUMBER(19,2),
    AMTDUE NUMBER,
    TRANSACTION_DATE VARCHAR2(30)
  );

我创建了一个过程来获取名称中包含 AMT 的字段。 但程序在执行时显示错误,我无法弄清楚为什么会这样 错误产生。

create or replace procedure chk_amt
(
    vtbl varchar2
)
as
tblcursor sys_refcursor;
tblsqlstr varchar2(1000);
importedrows VARCHAR2(1000); 
BEGIN
    tblsqlstr := 'Select COLUMN_NAME from user_tab_columns where table_name= '|| vtbl ||' and COLUMN_NAME like upper(''%AMT%'')' ;   
    OPEN tblcursor for tblsqlstr;
loop
fetch tblcursor into importedrows;
DBMS_OUTPUT.PUT_LINE(importedrows); 
EXIT WHEN tblcursor%NOTFOUND;
end loop;
CLOSE tblcursor;
end;
/

错误是

ORA-00904: "TEST_CASE": invalid identifier
ORA-06512: at "***.CHK_AMT", line 11
ORA-06512: at line 2

如何解决此错误?

2 个答案:

答案 0 :(得分:2)

除非您有使用动态SQL的特殊原因,否则使用静态SQL要容易得多。除非你有某些理由认为显式游标是有利的,否则使用隐式游标通常也更容易。由于您的代码段不显示需要使用动态SQL或显式游标,因此可以简化为

create or replace procedure chk_amt
(
    vtbl varchar2
)
as
BEGIN
  FOR columns IN (SELECT column_name
                    FROM user_tab_columns
                   WHERE table_name = vtbl
                     AND column_name LIKE '%AMT%')
  LOOP
    DBMS_OUTPUT.PUT_LINE(columns.column_name);
  END LOOP; 
end;

您获得的具体错误是将vtbl变量连接到动态SQL语句的结果。如果您要将字符串连接在一起,则需要在字符串中的变量之前和之后放置单引号,并且您必须转义表名中的任何单引号(当然,它可能存在表名中的任何单引号。如果必须使用动态SQL,那么使用绑定变量代替

会更好
tblsqlstr := 'Select COLUMN_NAME 
                from user_tab_columns 
                where table_name= :1 
                  and COLUMN_NAME like upper(''%AMT%'')' ;   
OPEN tblcursor for tblsqlstr using vtbl;

除了提高效率并避免SQL注入攻击的可能性之外,还可以避免转义本地变量中的数据,并且无需为字符串添加额外的引号。

答案 1 :(得分:2)

在vtbl中添加一些单引号给动态语句,它变为: -

tblsqlstr := 'Select COLUMN_NAME from user_tab_columns where table_name= '''|| vtbl ||''' and COLUMN_NAME like upper(''%AMT%'')' ;  

这意味着当你的代码运行时,如果vtbl的值为table_a,那么实际运行的语句将是'table_a'而不是table_a。

N.B。注意不要将此过程公开,因为它易受SQL注入攻击。理想情况下,您应该使用绑定变量。示例如下: -

CREATE OR REPLACE PROCEDURE chk_amt(vtbl VARCHAR2) AS
tblcursor    SYS_REFCURSOR;
tblsqlstr    VARCHAR2(1000);
importedrows VARCHAR2(1000);
BEGIN
tblsqlstr := 'Select COLUMN_NAME from user_tab_columns where table_name= :val_bnd and COLUMN_NAME like upper(''%AMT%'')';
OPEN tblcursor FOR tblsqlstr USING vtbl;
LOOP
    FETCH tblcursor
        INTO importedrows;
    dbms_output.put_line(importedrows);
    EXIT WHEN tblcursor%NOTFOUND;
END LOOP;
CLOSE tblcursor;
END;