我有一个表课程,其中包含列DepId和课程以及其他一些值。我需要在这个表中搜索一些(DepId,Course)。该集将在运行时决定。
我想在一个过程中编写一个查询来获取与上面集相关的所有记录。 例如,考虑表格有
之类的数据DepId Course ...
------------------
1 A
2 B
3 C
4 D
5 E
6 F
现在我只想搜索以下记录:
DepId Course ...
------------------
1 A
4 D
写上述查询的最有效方法是什么? 我正在考虑创建一个记录数组并将其传递给'IN'运算符。但我无法得到任何这方面的例子。有人可以指导我吗?
谢谢
答案 0 :(得分:2)
您的问题的答案是是,诸如ARRAYS
和COLLECTIONS
之类的标注变量是解决其中一个或两个中存在多个值的问题的可行数据类型输入和输出值。
另外一个好消息是,对一个简单示例(例如OP中的那个)的讨论与复杂的示例几乎相同。如果采用一些先进的规划设计,使用阵列构建的解决方案具有良好的可扩展性和动态性。
有一些名为ARRAYS
和ASSOCIATIVE ARRAYS
的实际集合类型。我选择使用NESTED TABLE TYPES
,因为它们可以直接SQL
查询。在某些方面,它们表现出“阵列式”行为。还有其他权衡可以通过Oracle参考资料进行研究。
应用于搜索COURSE TABLE
的查询将采用JOIN
条件而不是IN-LIST
方法。
使用STORED PROCEDURE
类型的对象可以改善数据库响应。过程调用中的查询可以利用和重用已编译的代码及其缓存的执行计划。
Oracle中有很多用于将变量存储到内存中的集合类型。每个都有优势和某种限制。 AskTom from Oracle有一个很好的例子,可以通过选择一个变量集合类型而不是另一个变量集合来打破开发人员的期望。
对于此解决方案,我选择使用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();
加载和显示INPUT参数数组(嵌套表)
以下内容可以从主程序中删除,并作为调用程序的一部分提供。
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;
实际搜索呼叫和输出
此操作的搜索部分现已隔离且动态。它不需要改变。所有更改都发生在调用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
答案 1 :(得分:0)
这是我过去在类似你的情况下使用过的东西。希望它有所帮助。
这种方法的主要好处是,如果你只传递一个参数,它仍会返回该单个参数的所有记录。这样,可以使用具有5个输入参数的单个存储过程来搜索所有输入组合。
只需调用传入集合中的存储过程,并应返回所有计算条件的值
存储过程:
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;
/