2012-04-15 90 views
1

我使用光标的语句:动态游标

SELECT NAME FROM STUDENT WHERE ROLL = 1; 

我用:

CURSOR C IS SELECT NAME FROM STUDENT WHERE ROLL = roll; 
--roll is a variable I receive via a procedure, and the procedure works fine for the received parameter. 

在执行这一点,我能够与卷= 1

检索所有记录

现在,我需要检索组(可能通过光标)的记录,就像:

SELECT NAME FROM STUDENT WHERE ROLL IN (2, 4, 6); 

但IN子句中的值仅在运行时间处已知。我应该怎么做?也就是说,有没有什么办法可以给游标的子句分配参数WHERE

我试过在游标声明中使用数组,但是弹出一个错误告诉类似于:标准类型不能使用

我使用:

CURSOR C IS SELECT NAME FROM STUDENT WHERE ROLL IN (rolls); 
--rolls is an array initialized with the required roll numbers. 
+0

你能给我们看光标吗? – Arion 2012-04-15 13:58:35

+0

@Arion:完成。编辑了我的问题。 – user980411 2012-04-15 14:01:22

+1

我相信这样做的正确方法是创建一个过程,其结果是[ref-cursor](http://stackoverflow.com/questions/1170548/get-resultset-from-oracle-stored-程序)。在执行该游标的过程中,调用输出引用游标的第二个过程。通过这种方式,您可以将参数传递给您调用的过程,从而允许您在运行时分配动态参数,同时避免使用可能允许进行SQL注入的动态SQL。 – xQbert 2012-04-15 14:49:17

回答

4

首先,我假设参数的操作实际上并未在STUDENT匹配表列的名称。如果您实际编写了您发布的声明,则roll将被解析为列的名称,而不是参数或局部变量,因此该声明将返回STUDENT表中的每一行,其中ROLL列的列为NOT NULL

CURSOR C 
    IS SELECT NAME 
     FROM STUDENT 
     WHERE ROLL = roll; 

其次,虽然可以使用@Gaurav Soni建议的动态SQL,但这样做会生成一堆不可共享的SQL语句。这将泛滥共享池,可能会将其他语句从缓存中老化,并且每次都使用大量的CPU来硬解析语句。 Oracle构建的前提是您要解析一次SQL语句,通常使用绑定变量,然后用绑定变量的不同值执行语句多次。 Oracle可以完成解析查询,生成查询计划,将查询放入共享池等的过程,只需执行一次,然后在重新执行查询时重新使用所有这些。如果您生成一堆永远不会再使用的SQL语句,因为您使用的是没有绑定变量的动态SQL,Oracle将最终花费大量时间缓存不会再次执行的SQL语句,推送有用的缓存语句这将在共享池中再次使用,这意味着您将在下次遇到这些查询时重新解析这些查询。

此外,你已经打开了自己的SQL注入攻击。攻击者可以利用该过程从任何表中读取任何数据或执行存储过程所有者有权访问的任何功能。即使您的应用程序不是特别注重安全性,这将会成为一个主要的安全漏洞。

你最好使用一个集合。这可以防止SQL注入攻击,并生成单个可共享的SQL语句,因此您不必进行持续的硬解析。

SQL> create type empno_tbl is table of number; 
    2/

Type created. 

SQL> create or replace procedure get_emps(p_empno_arr in empno_tbl) 
    2 is 
    3 begin 
    4 for e in (select * 
    5    from emp 
    6    where empno in (select column_value 
    7         from table(p_empno_arr))) 
    8 loop 
    9  dbms_output.put_line(e.ename); 
10 end loop; 
11 end; 
12/

Procedure created. 

SQL> set serveroutput on; 
SQL> begin 
    2 get_emps(empno_tbl(7369,7499,7934)); 
    3 end; 
    4/
SMITH 
ALLEN 
MILLER 

PL/SQL procedure successfully completed. 
+0

:先生,请你解释一下你的意思是不是可共享的sql – 2012-04-15 20:23:08

+0

@GauravSoni - 已更新我的回答 – 2012-04-16 03:24:09

+0

非常感谢你CH。 – user980411 2012-04-21 16:24:07

1
create or replace procedure dynamic_cur(p_empno VARCHAR2) IS 
cur  sys_refcursor; 
v_ename emp.ename%type; 
begin 
    open cur for 'select ename from emp where empno in (' || p_empno || ')'; 
    loop 
    fetch cur into v_ename; 
    exit when cur%notfound; 
    dbms_output.put_line(v_ename); 
    end loop; 
    close cur; 
end dynamic_cur; 

过程创建

运行procedure dynamic_cur

declare 
v_empno varchar2(200) := '7499,7521,7566'; 
begin 
    dynamic_cur(v_empno); 
end; 

输出

ALLEN 
WARD 
JONES 

注意:由于XQbert提到,dynamic cursor导致SQL injection,但如果你不工作的任何关键的要求,在不涉及安全,那么你可以使用这个。

+1

仅仅因为你正在做的任务不'如果你这样写代码,攻击者可以读取任何表中的任何数据,这些数据可以被程序的所有者访问 - 这是非常罕见的,不是一个安全问题,使用动态SQL也会产生大量不可共享的SQL语句,并且需要大量的硬解析 – 2012-04-15 17:58:01

+0

@justin:thnx用于评论先生我会记住你的话 – 2012-04-15 18:26:03

+0

非常感谢。 – user980411 2012-04-21 16:23:14

1

也许你可以传递一组引用逗号分隔值。 例如'1','2'等 如果将此值传递给varchar输入变量中的过程,则可以根据表匹配使用该值来获取多行。因此,光标 SELECT NAME FROM STUDENT WHERE ROLL IN

将被评估为 SELECT NAME FROM STUDENT WHERE ROLL IN('1','2');

希望它可以帮助

+0

非常感谢。 – user980411 2012-04-21 16:23:08