利用 Oracle 集合构建数组类型的解决方案
您的问题的答案是是的,ARRAYS 和 COLLECTIONS 等维度变量是解决输入和输出值中的一个或两个中存在多个值的问题的可行数据类型.
另外一个好消息是,对简单示例(例如 OP 中的示例)的讨论与对复杂示例的讨论几乎相同。使用数组构建的解决方案如果在设计时稍加提前规划,则具有很好的可扩展性和动态性。
一些前期设计决策
-
存在称为ARRAYS 和ASSOCIATIVE 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) 来表示 DepId 和 Course 的每个唯一组合。
请注意,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