oracle中动态向游标添加where子句

发布于 2024-07-09 18:49:21 字数 305 浏览 9 评论 0原文

我有 plsql 过程,它接受某些参数,例如 v_name、v_country、v_type。

我希望有一个带有这样的 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

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

I have plsql procedure which accepts certain parameters e.g. v_name, v_country, v_type.

I wish to have a cursor with a select statement like this:

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

If certain parameters are empty can I only add the relevant where clauses to the cursor? Or is there a better way to accomplish this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

初见 2024-07-16 18:49:21

使用它的最佳方法是使用 DBMS_SQL。

您创建一个代表 SQL 语句的字符串。 您仍然使用绑定变量。 这是痛苦的。

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

CREATE OR REPLACE FUNCTION find_country( v_name  t1.country%TYPE,
                                         v_type  t2.type%TYPE)  /* Hmm, column called type? */
DECLARE
  v_SQL         varchar2(2000);
  v_select          INTEGER;   /* "Pointer" to a DBMS_SQL select statement */
  v_execute         INTEGER;

BEGIN
  v_SQL := 'select column from table1 t1, table2 t2 ||
           'where t1.id = t2.id';

  IF v_name IS NOT NULL THEN
    v_SQL := v_SQL || ' AND t1.country = :v_name'
  END IF;

  IF v_type IS NOT NULL THEN
    v_SQL := v_SQL || ' AND t2.type = :v_type';
  END IF;

  /* Setup Cursor */
  v_select := dbms_sql.open_cursor;     
  dbms_sql.parse( v_select, v_SQL, DBMS_SQL.native);

  IF v_name IS NOT NULL THEN
    dbms_sql.bind_variable( v_select, ':v_name', v_name );
  END IF;

  IF v_type IS NOT NULL THEN
    dbms_sql.bind_variable( v_select, ':v_type', v_type );
  END IF;

  DBMS_SQL.DEFINE_COLUMN(v_select, 1, v_column);  /* This is what we have selected */

  /* Return value from EXECUTE is undefined for a SELECT */     
  v_execute := DBMS_SQL.EXECUTE( v_select );

  IF DBMS_SQL.FETCH_ROWS( v_select ) > 0 THEN

    /* A row was found  
    DBMS_SQL.COLUMN_VALUE( v_select, 1, v_column);

    /* Tidy Up */
    DBMS_SQL.CLOSE_CURSOR(v_select);

    RETURN v_ID_address;

  ELSE

     DBMS_SQL.CLOSE_CURSOR(v_select);

     /* No row */
     RETURN NULL;
  END IF;

  EXCEPTION
    WHEN OTHERS THEN
      IF DBMS_SQL.IS_open(v_select) THEN
        DBMS_SQL.CLOSE_CURSOR(v_select);
      END IF;
      RAISE;
END;

与仅仅编写内联 SQL 相比,这种方法是如此痛苦,除非你有大量的列,否则有时编写几个不同的版本会更容易使用这个语法:

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

The best way to use this is with DBMS_SQL.

You create a string that represents your SQL statement. You still use bind variables. It's painful.

It goes something like this (I haven't compiled this, but it should be close) :-

CREATE OR REPLACE FUNCTION find_country( v_name  t1.country%TYPE,
                                         v_type  t2.type%TYPE)  /* Hmm, column called type? */
DECLARE
  v_SQL         varchar2(2000);
  v_select          INTEGER;   /* "Pointer" to a DBMS_SQL select statement */
  v_execute         INTEGER;

BEGIN
  v_SQL := 'select column from table1 t1, table2 t2 ||
           'where t1.id = t2.id';

  IF v_name IS NOT NULL THEN
    v_SQL := v_SQL || ' AND t1.country = :v_name'
  END IF;

  IF v_type IS NOT NULL THEN
    v_SQL := v_SQL || ' AND t2.type = :v_type';
  END IF;

  /* Setup Cursor */
  v_select := dbms_sql.open_cursor;     
  dbms_sql.parse( v_select, v_SQL, DBMS_SQL.native);

  IF v_name IS NOT NULL THEN
    dbms_sql.bind_variable( v_select, ':v_name', v_name );
  END IF;

  IF v_type IS NOT NULL THEN
    dbms_sql.bind_variable( v_select, ':v_type', v_type );
  END IF;

  DBMS_SQL.DEFINE_COLUMN(v_select, 1, v_column);  /* This is what we have selected */

  /* Return value from EXECUTE is undefined for a SELECT */     
  v_execute := DBMS_SQL.EXECUTE( v_select );

  IF DBMS_SQL.FETCH_ROWS( v_select ) > 0 THEN

    /* A row was found  
    DBMS_SQL.COLUMN_VALUE( v_select, 1, v_column);

    /* Tidy Up */
    DBMS_SQL.CLOSE_CURSOR(v_select);

    RETURN v_ID_address;

  ELSE

     DBMS_SQL.CLOSE_CURSOR(v_select);

     /* No row */
     RETURN NULL;
  END IF;

  EXCEPTION
    WHEN OTHERS THEN
      IF DBMS_SQL.IS_open(v_select) THEN
        DBMS_SQL.CLOSE_CURSOR(v_select);
      END IF;
      RAISE;
END;

This approach is so painful compared to just writing the SQL inline that unless you have heaps of columns sometimes it's just easier writing a couple of different versions using this syntax:

FOR r IN (SELECT blah FROM blah WHERE t1 = v_t1) LOOP
   func( r.blah );
END LOOP;
你怎么这么可爱啊 2024-07-16 18:49:21

这不是您直接要求的,但它可能是一个可以接受的解决方案:

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)

It's not directly what you're asking, but it may be an acceptable solution:

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)
柳若烟 2024-07-16 18:49:21

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

One way would be to build up your query as a string then use execute immediate

傲世九天 2024-07-16 18:49:21

实现此目的的最佳方法是使用 Oracle 的应用程序上下文功能,该功能的最佳定义是最佳性能和安全性。

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

另一种编写速度最快但性能不佳的方法如下:

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)

The best way to do this would be to use Oracle's Application Context feature, best defined as best performance and security.

The faster way would be what hamishmcn suggested, using EXECUTE IMMEDIATE. I'd choose that over WW's suggestion of DBMS_SQL every time.

Another way that's quickest to write but won't perform as well would be something like this:

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)
你爱我像她 2024-07-16 18:49:21

您不必使用 dbms_sql 来解决此问题
并且您仍然可以通过使用引用光标来使用普通光标。

示例:

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”绑定变量来做得更好,如下所示:

open c1 for l_sql using v_name, v_country;

You do not have to use dbms_sql to solve this problem
and you can still use normal cursor by using a ref cursor.

Sample:

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;
/

You can make this better by binding the variables with the command 'using' like this:

open c1 for l_sql using v_name, v_country;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文