Oracle是否支持非标量游标参数?

时间:2018-05-07 20:44:29

标签: plsql cursor

这是关于Oracle PL / SQL的问题。

我有一个过程,在运行时之前不知道确切的WHERE子句:

DECLARE
    CURSOR my_cursor is 
    SELECT ...
    FROM ...
    WHERE terms in (
        (SELECT future_term2 FROM term_table),  -- whether this element should be included is conditional
        (SELECT future_term1 FROM term_table),
        (SELECT present_term FROM term_table)
    );
BEGIN
    (the processing)
END;
/

(SELECT ... FROM term_table)查询返回的是一个4个字符的字符串。

对于解决方案,我正在考虑使用参数化游标:

DECLARE
    target_terms SOME_DATATYPE;

    CURSOR my_cursor (pi_terms IN SOME_DATATYPE) IS 
        SELECT ...
        FROM ...
        WHERE terms in my_cursor.pi_terms;
BEGIN
    target_terms := CASE term_digit
    WHEN '2' THEN (
        (SELECT future_term2 FROM term_table),
        (SELECT future_term1 FROM term_table),
        (SELECT present_term FROM term_table)
    ) ELSE (
        (SELECT future_term1 FROM term_table),
        (SELECT present_term FROM term_table)
    )
    END;

    FOR my_record IN my_cursor (target_terms) LOOP
        (the processing)
    END LOOP;
END;
/

问题是我不知道SOME_DATATYPE的数据类型是什么,也不知道Oracle是否支持这样的游标参数。如果支持,上面显示的方法是否正确构造target_terms的值?如果没有,怎么样?

希望知道的人可以提供建议。非常感谢你的帮助。

2 个答案:

答案 0 :(得分:4)

您当然可以将参数传递给游标,就像您可以传递给函数一样 - 但只能传入IN参数。但是,PL / SQL是一种强类型语言,因此必须在编译时指定数据类型。

在我看来,您需要做的是动态构建查询然后使用

OPEN cursor FOR l_query;

其中l_query是构造的字符串。这应该让您对自己可以做的事情有所了解:

CREATE OR REPLACE PACKAGE return_id_sal
   AUTHID DEFINER
IS
   TYPE employee_rt IS RECORD
   (
      employee_id   employees.employee_id%TYPE,
      salary        employees.salary%TYPE
   );

   FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
      RETURN SYS_REFCURSOR;
END return_id_sal;
/

CREATE OR REPLACE PACKAGE BODY return_id_sal
IS
   FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
      RETURN SYS_REFCURSOR
   IS
      l_return   SYS_REFCURSOR;
   BEGIN
      OPEN l_return FOR
         'SELECT employee_id, salary FROM employees ' || append_to_from_in;

      RETURN l_return;
   END allrows_by;
END return_id_sal;
/

DECLARE
   l_cursor   SYS_REFCURSOR;
   l_row      return_id_sal.employee_rt;
BEGIN
   l_cursor := return_id_sal.allrows_by ('WHERE department_id = 10');

   LOOP
      FETCH l_cursor INTO l_row;

      EXIT WHEN l_cursor%NOTFOUND;
   END LOOP;
END;
/

您需要使用此类代码对SQL注入采取预防措施。当然,用户永远不能将SQL文本直接传递给这样的函数!

答案 1 :(得分:0)

您也可以使用一些内置的VARRAY SQL类型,如SYS.ODCIVARCHAR2LIST或创建自己的类型:

CREATE OR REPLACE NONEDITIONABLE TYPE VARCHARLIST
  AS VARRAY(32767) OF VARCHAR2(4000);

然后您可以在光标中使用SELECT COLUMN_VALUE FROM TABLE(COLLECTION)语句:

DECLARE
    l_terms SYS.ODCIVARCHAR2LIS; --or VARCHARLIST

    CURSOR my_cursor (p_terms IN SYS.ODCIVARCHAR2LIS) IS 
        SELECT your_column
        FROM your_table
        WHERE terms in (select COLUMN_VALUE from table (p_terms));
BEGIN
    select term 
    bulk collect into l_terms 
    from (
      select 'term1' term from dual
      union all
      select 'term2' term from dual
     );

    FOR my_record IN my_cursor (l_terms) LOOP
        --process data from your cursor...
    END LOOP;
END;