在oracle中将where子句动态添加到游标

在oracle中将where子句动态添加到游标,oracle,plsql,database-cursor,Oracle,Plsql,Database Cursor,我有一个plsql程序,它接受某些参数,例如v_名称、v_国家、v_类型 我希望有一个带有select语句的游标,如下所示: select column from table1 t1, table2 t2 where t1.name = v_name and t1.country = v_country and t1.id = t2.id and t2.type = v_type select column from table1 t1, table2 t2 where t1.name = n

我有一个plsql程序,它接受某些参数,例如v_名称、v_国家、v_类型

我希望有一个带有select语句的游标,如下所示:

select column from table1 t1, table2 t2
where t1.name = v_name
and t1.country = v_country
and t1.id = t2.id
and t2.type = v_type
select column from table1 t1, table2 t2
where t1.name = nvl(v_name, t1.name)
and t1.country = nvl(v_country, t1.country)
and t1.id = t2.id
and t2.type = nvl(v_type, t2.type)
open c1 for l_sql using v_name, v_country;

如果某些参数为空,我只能将相关where子句添加到游标中吗?或者有更好的方法来实现这一点吗?

一种方法是将查询建立为字符串,然后使用

最好的方法是使用DBMS\u SQL

创建一个表示SQL语句的字符串。您仍然使用绑定变量。很痛

它是这样的(我还没有编译这个,但应该很接近):-

与仅内联编写SQL相比,这种方法非常痛苦,除非您有大量的列,否则有时使用以下语法编写两个不同的版本会更容易:

FOR r IN (SELECT blah FROM blah WHERE t1 = v_t1) LOOP
   func( r.blah );
END LOOP;

这不是你要问的直接问题,但它可能是一个可接受的解决方案:

select column from table1 t1, table2 t2
where
    (v_name is null or t1.name = v_name)
and (v_country is null or t1.country = v_country)
and t1.id = t2.id
and (v_type is null or t2.type = v_type)

要做到这一点,最好的方法是使用Oracle的应用程序上下文功能,最好将其定义为最佳性能和安全性

更快的方法是hamishmcn建议的,使用executeimmediate。我会选择它,而不是WW每次建议的DBMS_SQL

另一种写得最快但性能不好的方法是:

select column from table1 t1, table2 t2
where t1.name = v_name
and t1.country = v_country
and t1.id = t2.id
and t2.type = v_type
select column from table1 t1, table2 t2
where t1.name = nvl(v_name, t1.name)
and t1.country = nvl(v_country, t1.country)
and t1.id = t2.id
and t2.type = nvl(v_type, t2.type)
open c1 for l_sql using v_name, v_country;

您不必使用dbms_sql来解决此问题 您仍然可以通过使用ref游标来使用普通游标

样本:

DECLARE
  TYPE cursor_ref IS REF CURSOR;
  c1 cursor_ref;
  r1 table1.column%type;
BEGIN
  l_sql := 'select t1.column from table1 t1, table2 t2 where t1.id = t2.id ';
  if v_name is not null then
    l_sql := l_sql||' and t1.name = '||v_name ;
  end if;
  if v_country is not null then
    l_sql := l_sql||' and t1.country = '||v_country';
  end if;
  if v_type is not null then  
    l_sql := l_sql||' and t2.type = '||v_type';
  end if;
  open c1 for l_sql;
  loop
      fetch c1 into r1;
      exit when c1%notfound;
      -- do something
  end loop;
  close c1;
end;
/
您可以通过如下方式使用命令“using”绑定变量,从而更好地实现这一点:

select column from table1 t1, table2 t2
where t1.name = v_name
and t1.country = v_country
and t1.id = t2.id
and t2.type = v_type
select column from table1 t1, table2 t2
where t1.name = nvl(v_name, t1.name)
and t1.country = nvl(v_country, t1.country)
and t1.id = t2.id
and t2.type = nvl(v_type, t2.type)
open c1 for l_sql using v_name, v_country;

一个名为type?的列。。。不要问(非常感谢这一点,看起来我必须这样做。对于数量有限的参数,我认为这是最好的解决方案。动态方法更通用,但也需要更多的处理,更难维护。这可能会导致Oracle生成错误的计划。请记住,此语句将有一个计划。它是g如果在t2.type或t1.name上使用任何索引时遇到问题,这可能是SQL注入错误,具体取决于“构建查询”的含义