oracle 如何在where in子句中使用关联数组?

nsc4cvqm  于 2022-12-03  发布在  Oracle
关注(0)|答案(2)|浏览(225)

要将ID列表/数组从C#传递到Oracle PL/SQL过程,you have to define an associative array type(例如,具有整数索引的表):

TYPE Ids_t_a is table of Number index by binary_integer;

我想写一个存储过程,它 Package 一个如下所示的查询:

SELECT Id, Name from Person where Id in Ids;

我知道基本的SQL,但不是PL/SQLMaven,表值类型超出了我的能力范围。我尝试过:

PROCEDURE GetData(Ids in Ids_t_a, results out sys_refcursor) IS
BEGIN
Open Results for
SELECT Id, Name from Person p where p.Id in Ids;
END;

但这会产生错误“expression is of wrong type”,这似乎是因为您无法以这种方式使用关联数组。
要对输入参数运行所需的查询,还需要执行哪些步骤?

xyhw6mcr

xyhw6mcr1#

在Oracle中,有两个范围可以评估语句:

  1. Oracle引擎将在其中分析SQL语句的SQL范围;和
  2. Oracle引擎将在其中分析过程语言语句(PL/SQL)的PL/SQL范围。
    关联数组是一种PL/SQL数据类型,只能在PL/SQL作用域中使用。它不能在SQL语句中使用,因此无法直接使用关联数组(并且由于某些未知原因,C#不支持传递非关联数组)。
    要对输入参数运行所需的查询,还需要执行哪些步骤?
    您需要将从C#传入的关联数组集合类型转换为PL/SQL作用域中的非关联数组集合类型(嵌套表集合类型或已在SQL作用域中定义的VARRAY集合类型),然后在SQL作用域中使用该非关联数组。
    您可以使用SYS.ODCINUMBERTYPE之类的内置集合类型,也可以在SQL作用域中定义自己的集合:
CREATE TYPE number_list IS TABLE OF NUMBER;

然后,您可以创建包和过程以及一个helper函数来执行转换:

CREATE PACKAGE your_package IS
  TYPE Ids_t_a is table of Number index by binary_integer;

  FUNCTION map_ids(
    Ids     in  Ids_t_a
  ) RETURN number_list PIPELINED;

  PROCEDURE GetData(
    Ids     in  Ids_t_a,
    results out sys_refcursor
  );
END;
/

然后包身:

CREATE PACKAGE BODY your_package IS
  FUNCTION map_ids(
    Ids     in  Ids_t_a
  ) RETURN number_list PIPELINED
  IS
    v_idx BINARY_INTEGER;
  BEGIN
    IF ids IS NULL THEN
      RETURN;
    END IF;
    v_idx := ids.FIRST;
    WHILE v_idx IS NOT NULL LOOP
      PIPE ROW(ids(v_idx));
      v_idx := ids.NEXT(v_idx);
    END LOOP;
  END;

  PROCEDURE GetData(
    Ids     in  Ids_t_a,
    results out sys_refcursor
  )
  IS
  BEGIN
    Open Results for
      SELECT Id, Name
      FROM   Person
      WHERE  Id MEMBER OF map_ids(ids);
  END;
END;
/
  • 注意:如果您使用VARRAY集合资料类型,例如内建的SYS.ODCINUMBERLIST类型,则无法使用MEMBER OF运算子,因为它只支援巢状表格集合类型。您必须改用子查询(或链接)和表格集合表示式。例如:Open Results for SELECT p.Id, p.Name FROM Person p INNER JOIN TABLE(map_ids(ids)) i ON p.id = i.COLUMN_VALUE;*

如果有示例数据:

CREATE TABLE person (id, name) AS
SELECT 1, 'Alice' FROM DUAL UNION ALL
SELECT 2, 'Beryl' FROM DUAL UNION ALL
SELECT 3, 'Carol' FROM DUAL UNION ALL
SELECT 4, 'Debra' FROM DUAL;

然后可以调用过程(从C#或PL/SQL块),传递关联数组并检索游标:

DECLARE
  v_cur  SYS_REFCURSOR;
  v_ids  YOUR_PACKAGE.IDS_T_A;
  v_id   PERSON.ID%TYPE;
  v_name PERSON.NAME%TYPE;
BEGIN
  v_ids(1) := 3;  -- Note: These are deliberately not sequential index values.
  v_ids(3) := 1;  --       Indexes generated by C# probably would be, but it 
  v_ids(42) := 7; --       is not guaranteed to always be true.
  YOUR_PACKAGE.GetData(v_ids, v_cur);
  LOOP
    FETCH v_cur INTO v_id, v_name;
    EXIT WHEN v_cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(v_id || ': ' || v_name);
  END LOOP;
END;
/

输出:

1: Alice
3: Carol

fiddle

s3fp2yjn

s3fp2yjn2#

Oracle提供了一个内置类型(sys.odcinumberlist),它能够包含数字(这样您就不必创建自己的类型);这就是我的示例所做的,我使用的是Scott的示例模式; emp表包含一些在某些 * 部门 * 工作的 * 雇员 * -部门列表将被传递给过程。

SQL> create or replace procedure getdata
  2    (ids in sys.odcinumberlist, results out sys_refcursor)
  3  is
  4  begin
  5    open results for
  6      select empno, ename from emp
  7      where deptno in (select * from table(ids));   --> this is what you're looking for
  8  end;
  9  /

Procedure created.

让我们试试看:

SQL> set serveroutput on
SQL> declare
  2    l_ids   sys.odcinumberlist;  -- it'll contain list of departments
  3    l_rc    sys_refcursor;       -- refcursor will be returned into this variable
  4    l_empno number;              -- employee number
  5    l_ename varchar2(20);        -- their name
  6  begin
  7    getdata(sys.odcinumberlist(10, 20), l_rc);   -- this is how you call the procedure
  8
  9    loop                                         -- you'll be using the result differently; I'm just
 10      fetch l_rc into l_empno, l_ename;          -- displaying contents
 11      exit when l_rc%notfound;
 12      dbms_output.put_line(l_empno ||' - '|| l_ename);
 13    end loop;
 14  end;
 15  /
7782 - CLARK
7839 - KING
7934 - MILLER
7369 - SMITH
7566 - JONES
7788 - SCOTT
7876 - ADAMS
7902 - FORD

PL/SQL procedure successfully completed.

SQL>

当然,您可以使用自己的类型;只是我不想创建一个包并在那里做所有的事情,因为type(您发布的那个)不能在SQL级别创建:

SQL> create or replace TYPE Ids_t_a is table of Number index by binary_integer;
  2  /

Warning: Type created with compilation errors.

SQL> show err
Errors for TYPE IDS_T_A:

LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0      PL/SQL: Compilation unit analysis terminated
1/17     PLS-00355: use of pl/sql table not allowed in this context
SQL>

相关问题