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

Does Oracle support non-scalar cursor parameter?

这是一个关于 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) 查询 returns 是一个 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 值的方法是否正确?如果没有,怎么办?

希望知道的人指教。非常感谢您的帮助。

您当然可以将参数传递给游标,就像传递给函数一样——但只能传递 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 文本直接传递给这样的函数!

您也可以使用一些内置的 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;