1

这在我只有一个状态代码作为参数时有效。

当 parm_list 中有多个 state_code 时,如何让代码工作?

要求:

(1)我不想在光标定义中硬编码状态码

(2) 我确实想在 where 子句中允许多个州代码

例如:我想为parm_list = ('NY','NJ','NC')运行此代码。我在协调 parm_list 中的单引号与 'where state_code in ' 查询中的单引号时遇到了困难。

set serveroutput on;

DECLARE
parm_list varchar2(40);

cursor get_state_codes(in_state_codes varchar2)
is
select state_name, state_code from states
where state_code in (in_state_codes);

BEGIN
 parm_list := 'NY';
 for get_record in get_state_codes(parm_list) loop
  dbms_output.put_line(get_record.state_name || get_record.state_code);
 end loop;
END;
4

2 回答 2

11

从编码的角度来看,使用动态 SQL 是最简单的方法。但是,动态 SQL 的问题在于,您必须对查询的每个不同版本进行硬解析,这不仅可能会占用您的 CPU,而且可能会用大量不可共享的 SQL 语句淹没您的共享池,从而推动out 您想要缓存的语句,导致更多的硬解析和共享池碎片错误。如果您每天运行一次,这可能不是主要问题。如果数百人每天执行数千次,这可能是一个主要问题。

动态 SQL 方法的示例

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_deptnos  varchar2(100) := '10,20';
  3    l_rc       sys_refcursor;
  4    l_dept_rec dept%rowtype;
  5  begin
  6    open l_rc for 'select * from dept where deptno in (' || l_deptnos || ')';
  7    loop
  8      fetch l_rc into l_dept_rec;
  9      exit when l_rc%notfound;
 10      dbms_output.put_line( l_dept_rec.dname );
 11    end loop;
 12    close l_rc;
 13* end;
SQL> /
ACCOUNTING
RESEARCH

PL/SQL procedure successfully completed.

或者,您可以使用集合。这具有生成单个可共享游标的优势,因此您不必担心硬解析或淹没共享池。但它可能需要更多的代码。处理集合的最简单方法

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_deptnos  tbl_deptnos := tbl_deptnos(10,20);
  3  begin
  4    for i in (select *
  5                from dept
  6               where deptno in (select column_value
  7                                  from table(l_deptnos)))
  8    loop
  9      dbms_output.put_line( i.dname );
 10    end loop;
 11* end;
SQL> /
ACCOUNTING
RESEARCH

PL/SQL procedure successfully completed.

另一方面,如果您真的必须以逗号分隔的值列表开始,那么您必须先将该字符串解析为一个集合,然后才能使用它。解析分隔字符串有多种方法——我个人最喜欢的是在分层查询中使用正则表达式,但您当然也可以编写程序方法

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_deptnos     tbl_deptnos;
  3    l_deptno_str  varchar2(100) := '10,20';
  4  begin
  5    select regexp_substr(l_deptno_str, '[^,]+', 1, LEVEL)
  6      bulk collect into l_deptnos
  7      from dual
  8   connect by level <= length(replace (l_deptno_str, ',', NULL));
  9    for i in (select *
 10                from dept
 11               where deptno in (select column_value
 12                                  from table(l_deptnos)))
 13    loop
 14      dbms_output.put_line( i.dname );
 15    end loop;
 16* end;
 17  /
ACCOUNTING
RESEARCH

PL/SQL procedure successfully completed.
于 2012-06-14T21:48:10.050 回答
7

一种选择是使用 INSTR 而不是 IN:

SELECT uo.object_name
      ,uo.object_type
FROM   user_objects uo
WHERE  instr(',TABLE,VIEW,', ',' || uo.object_type || ',') > 0;

尽管这看起来很难看,但它运行良好,只要不使用正在测试的列上的索引(因为这会阻止使用任何索引),性能不会受到太大影响。例如,如果正在测试的列是主键,那么绝对不应该使用它。

另一种选择是:

SELECT uo.object_name
      ,uo.object_type
FROM   user_objects uo
WHERE  uo.object_type IN
       (SELECT regexp_substr('TABLE,VIEW', '[^,]+', 1, LEVEL)
        FROM   dual
        CONNECT BY regexp_substr('TABLE,VIEW', '[^,]+', 1, LEVEL) IS NOT NULL);

在这种情况下,值列表应该连接成一个 varchar 变量,用逗号(或任何你喜欢的东西)分隔。

于 2012-06-15T04:28:38.830 回答