在 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 集合构建数组类型的解决方案
您的问题的答案是是的,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
【讨论】:
【参考方案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”运算符中使用记录数组的主要内容,如果未能解决你的问题,请参考以下文章