在 Oracle 的“IN”运算符中使用记录数组

Posted

技术标签:

【中文标题】在 Oracle 的“IN”运算符中使用记录数组【英文标题】:Using array of Records in 'IN' operator in Oracle 【发布时间】:2014-03-26 17:08:50 【问题描述】:

我有一个表 Course,其中包含列 DepId 和 Course 以及其他一些值。我需要在此表中搜索一组(DepId,Course)。这个集合将在运行时决定。

我想在一个过程中编写一个查询来获取与上述集合相关的所有记录。 例如考虑表有类似的数据

DepId Course  ...
------------------
   1       A
   2       B
   3       C
   4       D
   5       E
   6       F

现在只想搜索以下记录:

DepId Course  ...
------------------
   1       A
   4       D

编写上述查询的最有效方法是什么? 我正在考虑创建一个记录数组并将其传递给“IN”运算符。但是我无法为此提供任何示例。有人可以指导我吗?

谢谢

【问题讨论】:

【参考方案1】:

利用 Oracle 集合构建数组类型的解决方案

您的问题的答案是是的ARRAYSCOLLECTIONS 等维度变量是解决输入和输出值中的一个或两个中存在多个值的问题的可行数据类型.

另外一个好消息是,对简单示例(例如 OP 中的示例)的讨论与对复杂示例的讨论几乎相同。使用数组构建的解决方案如果在设计时稍加提前规划,则具有很好的可扩展性和动态性。

一些前期设计决策

存在称为ARRAYSASSOCIATIVE ARRAYS 的实际集合类型。我选择使用NESTED TABLE TYPES 是因为它们可以直接使用SQL 查询。在某些方面,它们表现出“类似数组”的行为。还有其他权衡可以通过 Oracle 参考资料进行研究。

应用于搜索COURSE TABLE 的查询将应用JOIN 条件而不是IN-LIST 方法。

使用STORED PROCEDURE 类型的对象可以提高数据库响应。过程调用中的查询可以利用和重用已编译的代码及其缓存的执行计划。

选择正确的集合或数组类型

Oracle 中有很多集合类型可供选择,用于将变量存储到内存中。每个都有优点和某种限制。 AskTom from Oracle 提供了一个很好的示例,并详细说明了开发人员通过选择一种变量集合类型而不是另一种变量集合类型可以期待什么。

使用 NESTED TABLE 类型管理多值变量

对于这个解决方案,我选择使用NESTED TABLES,因为它们可以通过SQL 命令直接访问。在尝试了几种不同的方法后,我注意到纯 SQL 可访问性使生成的代码更加清晰。

不利的一面是,您会注意到在声明嵌套表类型的实例、初始化每个实例以及使用添加新值。

在任何情况下,如果您预计输入变量或值(我们的输出)的数量未知,则任何类型的数组类型数据类型(集合)对于您的代码来说都是一种更灵活的结构。最终可能需要更少的维护。

示例:存储过程搜索查询

自定义类型定义

 CREATE OR REPLACE TYPE  "COURSE_REC_TYPE" IS OBJECT (DEPID NUMBER(10,0), COURSE VARCHAR2(10));

 CREATE OR REPLACE TYPE  "COURSE_TBL_TYPE" IS TABLE of course_rec_type;

程序源代码

 create or replace PROCEDURE ZZ_PROC_COURSE_SEARCH IS

    my_input   course_tbl_type:= course_tbl_type();
    my_output  course_tbl_type:= course_tbl_type();
    cur_loop_counter   pls_integer;

    c_output_template   constant  varchar2(100):=
        'DEPID: <<DEPID>>,  COURSE: <<COURSE>>';
    v_output   VARCHAR2(200);

    CURSOR find_course_cur IS           
       SELECT crs.depid, crs.course
         FROM zz_course crs,
             (SELECT depid, course
                FROM TABLE (CAST (my_input AS course_tbl_type))
                ) search_values
        WHERE crs.depid = search_values.depid
          AND crs.course = search_values.course;

 BEGIN
    my_input.extend(2);
    my_input(1):= course_rec_type(1, 'A');
    my_input(2):= course_rec_type(4, 'D');

    cur_loop_counter:= 0;
    for i in find_course_cur
    loop
       cur_loop_counter:= cur_loop_counter + 1;
       my_output.extend;
       my_output(cur_loop_counter):= course_rec_type(i.depid, i.course);

    end loop;

 for j in my_output.first .. my_output.last
 loop
     v_output:= replace(c_output_template, '<<DEPID>>', to_char(my_output(j).depid));
     v_output:= replace(v_output, '<<COURSE>>', my_output(j).course);

     dbms_output.put_line(v_output);

 end loop;

 end ZZ_PROC_COURSE_SEARCH;

程序输出:

 DEPID: 1,  COURSE: A
 DEPID: 4,  COURSE: D

 Statement processed.


 0.03 seconds

我的评论:我对输入变量的存储方式不是特别满意。将值“加载”到嵌套表结构中存在一种笨拙的问题......如果您可以考虑使用单个搜索键而不是复合对(即 depid 和 course),则问题会浓缩为更简单的形式。

使用单个搜索值修改光标

这是对 OP 表设计的建议修改。添加单个唯一键 ID 列 (RecId) 来表示 DepIdCourse 的每个唯一组合。

请注意,RecId 列表示 SURROGATE KEY,除了作为唯一分配值的属性之外,它应该没有任何内部含义。

自定义类型定义

 CREATE OR REPLACE TYPE  "NUM_TBL_TYPE" IS TABLE of INTEGER;

删除数组变量

这将直接通过过程调用的输入参数传递。

 -- REMOVE
 my_input   course_tbl_type:= course_tbl_type();

加载和呈现输入参数数组(嵌套表)

可以从主过程中删除以下内容,并作为对过程的调用的一部分。

 BEGIN
    my_input.extend(2);
    my_input(1):= course_rec_type(1, 'A');
    my_input(2):= course_rec_type(4, 'D');

变成:

 create or replace PROCEDURE ZZ_PROC_COURSE_SEARCH (p_search_ids IN num_tbl_type) IS...

 my_external_input.extend(2);
 my_external_input:= num_tbl_type(1, 4);

更改内部光标定义

光标看起来差不多。由于只有一个搜索参数,您可以轻松使用 IN-LIST

 CURSOR find_course_cur IS           
    SELECT crs.depid, crs.course
      FROM zz_course_new crs, 
           (SELECT column_value as recid
              FROM TABLE (CAST (p_search_ids AS num_tbl_type))
           ) search_values
     WHERE crs.recid = search_values.recid;

实际的 SEARCH 调用和输出

此操作的搜索部分现在是独立且动态的。它不需要更改。所有更改都发生在调用 PL/SQL 块中,其中搜索 ID 值更易于读取和更改。

 DECLARE
    my_input_external   num_tbl_type:= num_tbl_type();

 BEGIN
    my_input_external.extend(3);
    my_input_external:= num_tbl_type(1,3,22);

    ZZ_PROC_COURSE_SEARCH (p_search_ids => my_input_external);

 END; 


 -- The OUTPUT (Currently set to DBMS_OUT)


 DEPID: 1,  COURSE: A
 DEPID: 4,  COURSE: D
 DEPID: 7,  COURSE: G

 Statement processed.

 0.01 seconds

【讨论】:

【参考方案2】:

这是我过去在与您类似的情况下使用过的东西。希望对您有所帮助。

这种方法的主要好处是,如果您只传递一个参数,它仍然会返回该单个参数的所有记录。这样,一个带有 5 个输入参数的存储过程就可以用于搜索所有输入组合。

只需调用传入集合的存储过程,并应返回所有符合条件的值

usp_custom_search '1','A' usp_custom_search '4','D' usp_custom_search '4',NULL usp_custom_search NULL,'A' 等

存储过程:

CREATE OR REPLACE PROCEDURE custom_search (
    dep_id IN VARCHAR2,
    course_id IN VARCHAR2,
    result_set OUT SYS_REFCURSOR)
BEGIN
    query_str VARCHAR2(1000);
    query_str := 'SELECT';
    query_str := query_str || ' DepId, Course';
    query_str := query_str || ' FROM Course';
    query_str := query_str || ' WHERE 1=1';
    IF (dep_id is not null) then query_str := query_str || ' AND DepId = ''' || dep_id || ''''; END IF;
    IF (course_id is not null) then query_str := query_str || ' AND Course = ''' || course_id || ''''; END IF;
    open result_set for query_str;
END custom_search;
/

【讨论】:

以上是关于在 Oracle 的“IN”运算符中使用记录数组的主要内容,如果未能解决你的问题,请参考以下文章

Oracle In子句

IN() 子句 oracle PLSQL 中的数组

Oracle In(匹配)子句

Oracle In(匹配)子句

如何在使用 SQLi 的 SQL IN 运算符的准备语句中使用 php 数组? [复制]

oracle数组 拼到sql的in子句中去