借助内存表处理复杂的 oracle 查询要求 . 在日常业务处理过程中 , 我们经常会碰到一些非常规的查询需求 , 这些需求我们或者可以借助动态语句 , 或者其他现有的 oracle 函数完成查询结果 , 但效率往往差强人意 . 假设我们有一个客户订单业务表 { 订单号 , 订
借助内存表处理复杂的oracle查询要求.
在日常业务处理过程中,我们经常会碰到一些非常规的查询需求,这些需求我们或者可以借助动态语句,或者其他现有的oracle函数完成查询结果,但效率往往差强人意.
假设我们有一个客户订单业务表{订单号,订单客户,订单日期,数量,金额}存储了订单的往来明细数据,订单表中保存最近3个月的往来明细共1000w条记录,其中客户总量约500000.并假定在订单表上有针对日期和客户的单独索引.
现在要求提供对任意集合的多个客户的某段时间的订单明细数据.
Select订单号,订单客户,订单日期,数量,金额
From订单业务表
Where订单日期between开始日期and结束日期
And订单客户in (客户1,客户2,客户3…)
面对这种需求,我们可以要求前台程序传回三个参数,开始日期,结束日期,客户列表(类似于客户1,客户2,客户3,客户4…)
一,创建测试用表.
Create table t_order_cust(
O_id varchar2(20),
O_customer varchar2(20),
O_date date,
O_qty numeric(18,2),
O_amount numeric(18,2)
);
Create index ind_t_order_cust_01 on t_order_cust(o_customer);
Create index ind_t_order_cust_02 on t_order_cust(o_date);
二,方法1 ,使用动态语句拼凑实现.
针对上述查询,我们可以拼凑动态语句实现,如下代码所示.
Declare
V_beg_date date := trunc(sysdate,’month’);
V_end_date date := trunk(sysdate);
V_cust_str varchar2(1000) := ‘’’客户1’’, ’’客户2’’, ’’客户3’’…’;
V_sql_str varchar2(2000) ;
Begin
V_sql_str := ‘select * from t_order_cust
Where o_date between ’ || v_beg_date || ‘ and ’ || v_end_date ||’
And o_customer in (’||v_cust_str||’)’;
Execute immediate v_sql_str;
End;
根据表明细数据的特点我们知道,客户索引的选择性为1000w/50w= 20,而日期索引的选择性为1000w/(3*30) = 10w,明显使用日期索引效率极差,我们只能选择使用客户上的索引,使用这种处理方法的优势是可以用到客户上的索引,但in使用索引的效率相对较差,并且这种处理方式下, oracle每次执行查询都需要重新建立查询执行树,也是需要一定的额外开销.
三,方法2使用like查询
除了上面的拼凑动态执行语句的方法之外,我们可以设想的到的第二种方法就是借助于oracle提供的like功能.如下代码所示.这种处理方式下对客户列表字符串的要求跟方法一少有区别.
Declare
V_beg_date date := trunc(sysdate,’month’);
V_end_date date := trunk(sysdate);
V_cust_str varchar2(1000) := ‘客户1,客户2,客户3…’;
Begin
select * from t_order_cust
Where o_date between v_beg_dateandv_end_date
And v_cust_str like ‘%’||o_customer||’%’;
End;
这种处理方式的优点在于代码书写简单,但由于对客户索引所在字段o_customer做了拼接处理||,所以将导致客户索引ind_t_order_cust_01无效,而只能使用效率较差的日期索引.在数据量较小,对效率无法造成影响时这种方法可以接受,但数据量较大时,这种方法的缺点将是致命的.
四,方法3使用instr函数处理
Declare
V_beg_date date := trunc(sysdate,’month’);
V_end_date date := trunk(sysdate);
V_cust_str varchar2(1000) := ‘客户1,客户2,客户3…’;
Begin
select * from t_order_cust
Where o_date between v_beg_dateandv_end_date
And instr(v_cust_str like ,o_custome) >0
End;
这种处理方式的优缺点跟使用like相似,同样由于对o_customer使用了函数,导致该索引不可用,函数索引同样也不适用于这种情况.
五,方法4使用内存表处理
我们知道, oracle, sqlserver等关系数据库最善于处理的数据类型是集合,而不是单独的记录.同样的100条记录,如果逐条循环处理和批量处理其效率的差别将是几何单位的.
所以,为了提高查询效率,我们这里考虑将给定的客户字符串转变为一个集合或者临时表来处理. Oracle使用全局临时表和复杂数据类型集合来支持这一点.
这里我们介绍一下使用复杂数据类型集合来处理的方式.
首先我们定义一个复杂类型.
create or replace type ctl.type_jax_varc2tab is table of varchar2(2000);
然后定义一个函数实现将给定的字符串转换为嵌套内存表.
CREATEORREPLACEFUNCTIONf_jax_str2tab(p_strINVARCHAR2,
p_sepvarchar2default',')RETURNctl.type_jax_varc2tabIS/******************************************************************
Ver1.0 Created by jaxzhang on 2009-06-08把字符串(1*2*3*4*5)转换为内存表形式create or replace type type_jax_varc2tab is table of varchar2(2000);测试用例:SELECT * FROM TABLE(f_jax_str2tab('1*2*3*4*5','*'));
******************************************************************/v_strvarchar2(2000);
v_cntNUMBER;
v_numtab type_jax_varc2tab := type_jax_varc2tab();--返回内存表BEGINselectdecode(substr(p_str,-1),p_sep,p_str,p_str || p_sep)intov_strfromdual;selectlength(v_str) - length(REPLACE(v_str, p_sep))intov_cntfromdual;FORiIN1.. v_cntLOOPv_numtab.EXTEND;
v_numtab(i) := substr(v_str,1, instr(v_str, p_sep) -1);
v_str := substr(v_str, instr(v_str,p_sep) +1);ENDLOOP;RETURNv_numtab;EXCEPTIONWHENOTHERSTHENv_numtab.DELETE;END;上述函数的功能就是要将类似于’客户1,客户2,客户3’的字符串转换为如下形式.
SELECT*FROMTABLE(f_jax_str2tab('客户1,客户2,客户3',','));
COLUMN_VALUE
客户1
客户2
客户3
得到上述的内存表之后,我们就可以使用类似于一个表或者视图的方式来与正式表t_order_cust关联得到我们需要的查询结果.
Select /*+ ordered use_nl(a b)*/
FromTABLE(f_jax_str2tab('客户1,客户2,客户3',',')) a,
T_order_cust b
Where b.o_customer = a.column_value
本文原创发布php中文网,转载请注明出处,感谢您的尊重!