一﹑Oracle ROLLUP和CUBE 用法解析
Oracle的GROUP BY语句除了最基本的语法外,还支持ROLLUP和CUBE语句。 如果是ROLLUP(A, B, C)的话,首先会对(A、B、C)进行GROUP BY,然后对(A、B)进行GROUP BY,然后是(A)进行GROUP BY,最后对全表进行GROUP BY操作。如果是GROUP BY CUBE(A, B, C),则首先会对(A、B、C)进行GROUP BY,然后依次是(A、B),(A、C),(A),(B、C),(B),(C),最后对全表进行GROUP BY操作。 grouping_id()可以美化效果:
Oracle的GROUP BY语句除了最基本的语法外,还支持ROLLUP和CUBE语句。
SQL> create table t as select * from dba_indexes;
表已创建。
SQL> select index_type, status, count(*) from t group by index_type, status; INDEX_TYPE STATUS COUNT(*)
--------------------------- -------- ---------- LOB VALID 51 NORMAL N/A 25 NORMAL VALID 479 CLUSTER VALID 11
下面来看看ROLLUP和CUBE语句的执行结果。
SQL> select index_type, status, count(*) from t group by rollup(index_type, status); INDEX_TYPE STATUS COUNT(*)
--------------------------- -------- ---------- LOB VALID 51 LOB 51 NORMAL N/A 25 NORMAL VALID 479 NORMAL 504 CLUSTER VALID 11 CLUSTER 11 566
已选择8行。 执行
SQL> select index_type, status, count(*) from t group by cube(index_type, status);
INDEX_TYPE STATUS COUNT(*)
--------------------------- -------- ---------- 566 N/A 25 VALID 541 LOB 51 LOB VALID 51 NORMAL 504 NORMAL N/A 25 NORMAL VALID 479 CLUSTER 11 CLUSTER VALID 11
已选择10行。
查询结果不是很一目了然,下面通过Oracle提供的函数GROUPING来整理一下查询结果。 SQL> select grouping(index_type) g_ind, grouping(status) g_st, index_type, status,
count(*)
2 from t group by rollup(index_type, status) order by 1, 2;
G_IND G_ST INDEX_TYPE STATUS COUNT(*)
---------- ---------- --------------------------- -------- ---------- 0 0 LOB VALID 51 0 0 NORMAL N/A 25 0 0 NORMAL VALID 479 0 0 CLUSTER VALID 11 0 1 LOB 51 0 1 NORMAL 504 0 1 CLUSTER 11 1 1 566
已选择8行。
这个查询结果就直观多了,和不带ROLLUP语句的GROUP BY相比,ROLLUP增加了对INDEX_TYPE的GROUP BY统计和对所有记录的GROUP BY统计。
也就是说,如果是ROLLUP(A, B, C)的话,首先会对(A、B、C)进行GROUP BY,然后对(A、B)进行GROUP BY,然后是(A)进行GROUP BY,最后对全表进行GROUP BY操作。 下面看看CUBE语句。
SQL> select grouping(index_type) g_ind, grouping(status) g_st, index_type, status,
count(*)
2 from t group by cube(index_type, status) order by 1, 2;
G_IND G_ST INDEX_TYPE STATUS COUNT(*)
---------- ---------- --------------------------- -------- ---------- 0 0 LOB VALID 51 0 0 NORMAL N/A 25 0 0 NORMAL VALID 479 0 0 CLUSTER VALID 11 0 1 LOB 51 0 1 NORMAL 504 0 1 CLUSTER 11 1 0 N/A 25 1 0 VALID 541 1 1 566
已选择10行。
和ROLLUP相比,CUBE又增加了对STATUS列的GROUP BY统计。
如果是GROUP BY CUBE(A, B, C),则首先会对(A、B、C)进行GROUP BY,然后依次是(A、B),(A、C),(A),(B、C),(B),(C),最后对全表进行GROUP BY操作。 除了使用GROUPING函数,还可以使用GROUPING_ID来标识GROUP BY结果。
SQL> select grouping_id(index_type, status) g_ind, index_type, status, count(*) 2 from t group by rollup(index_type, status) order by 1; G_IND INDEX_TYPE STATUS COUNT(*)
---------- --------------------------- -------- ---------- 0 LOB VALID 51 0 NORMAL N/A 25
0 NORMAL VALID 479 0 CLUSTER VALID 11 1 LOB 51 1 NORMAL 504 1 CLUSTER 11 3 566
已选择8行。
SQL> select grouping_id(index_type, status) g_ind, index_type, status, count(*) 2 from t group by cube(index_type, status) order by 1; G_IND INDEX_TYPE STATUS COUNT(*)
---------- --------------------------- -------- ---------- 0 LOB VALID 51 0 NORMAL N/A 25 0 NORMAL VALID 479 0 CLUSTER VALID 11 1 LOB 51 1 NORMAL 504 1 CLUSTER 11 2 N/A 25 2 VALID 541 3 566
已选择10行。
grouping_id()可以美化效果:
select DECODE(GROUPING_ID(C1), 1, '合计', C1) D1, DECODE(GROUPING_ID(C1, C2), 1, '小计', C2) D2,
DECODE(GROUPING_ID(C1, C2, C1 + C2), 1, '小计', C1 + C2) D3, count(*),
GROUPING_ID(C1, C2, C1 + C2, C1 + 1, C2 + 1), GROUPING_ID(C1) from T2
group by rollup(C1, C2, C1 + C2, C1 + 1, C2 + 1);
===========================================================
1.报表合计专用的Rollup函数 销售报表 广州 1月 2000元 广州 2月 2500元 广州 4500元 深圳 1月 1000元 深圳 2月 2000元 深圳 3000元 所有地区 7500元
以往的查询SQL:
Select area,month,sum(money) from SaleOrder group by area,month 然后广州,深圳的合计和所有地区合计都需要在程序里自行累计
1.其实可以使用如下SQL:
Select area,month,sum(total_sale) from SaleOrder group by rollup(area,month) 就能产生和报表一模一样的纪录
2.如果year不想累加,可以写成
Select year,month,area,sum(total_sale) from SaleOrder group by year, rollup(month,area) 另外Oracle 9i还支持如下语法:
Select year,month,area,sum(total_sale) from SaleOrder group by rollup((year,month),area)
3.如果使用Cube(area,month)而不是RollUp(area,month),除了获得每个地区的合计之外,还将获得每个月份的合计,在报表最后显示。
4.Grouping让合计列更好读
RollUp在显示广州合计时,月份列为NULL,但更好的做法应该是显示为\"所有月份\" Grouping就是用来判断当前Column是否是一个合计列,1为yes,然后用Decode把它转为\"所有月份\"
Select Decode(Grouping(area),1,'所有地区',area) area, Decode(Grouping(month),1,'所有月份',month), sum(money) From SaleOrder Group by RollUp(area,month);
2.对多级层次查询的start with.....connect by 比如人员组织,产品类别,Oracle提供了很经典的方法
SELECT LEVEL, name, emp_id,manager_emp_id FROM employee START WITH manager_emp_id is null CONNECT BY PRIOR emp_id = manager_emp_id;
上面的语句demo了全部的应用,start with指明从哪里开始遍历树,如果从根开始,那么它的manager应该是Null,如果从某个职员开始,可以写成emp_id='11' CONNECT BY 就是指明父子关系,注意PRIOR位置 另外还有一个LEVEL列,显示节点的层次
3.更多报表/分析决策功能 3.1 分析功能的基本结构
分析功能() over( partion子句,order by子句,窗口子句) 概念上很难讲清楚,还是用例子说话比较好.
3.2 Row_Number 和 Rank, DENSE_Rank 用于选出Top 3 sales这样的报表
当两个业务员可能有相同业绩时,就要使用Rank和Dense_Rank 比如
金额 RowNum Rank Dense_Rank 张三 4000元 1 1 1 李四 3000元 2 2 2 钱五 2000元 3 3 3 孙六 2000元 4 3 3 丁七 1000元 5 5 4
这时,应该把并列第三的钱五和孙六都选进去,所以用Ranking功能比RowNumber保险.至于Desnse还是Ranking就看具体情况了。
SELECT salesperson_id, SUM(tot_sales) sp_sales, RANK( ) OVER (ORDER BY SUM(tot_sales) DESC) sales_rank FROM orders GROUP BY salesperson_id 3.3 NTILE 把纪录平分成甲乙丙丁四等
比如我想取得前25%的纪录,或者把25%的纪录当作同一个level平等对待,把另25%当作另一个Level平等对待
SELECT cust_nbr, SUM(tot_sales) cust_sales, NTILE(4) OVER (ORDER BY SUM(tot_sales) DESC) sales_quartile FROM orders GROUP BY cust_nbr ORDER BY 3,2 DESC; NTITLE(4)把纪录以 SUM(tot_sales)排序分成4份.
3.4 辅助分析列和Windows Function
报表除了基本事实数据外,总希望旁边多些全年总销量,到目前为止的累计销量,前后三个月的平均销量这样的列来参考.
这种前后三个月的平均和到目前为止的累计销量就叫windows function, 见下例
SELECT month, SUM(tot_sales) monthly_sales, SUM(SUM(tot_sales)) OVER (ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_preceeding FROM orders GROUP BY month ORDER BY month;
SELECT month, SUM(tot_sales) monthly_sales, AVG(SUM(tot_sales)) OVER (ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) rolling_avg FROM orders GROUP BY month ORDER BY month;
Windows Function的关键就是Windows子句的几个取值 1 PRECEDING 之前的一条记录 1 FOLLOWING 之后的一条记录
UNBOUNDED PRECEDING 之前的所有记录 CURRENT ROW 当前纪录
4.SubQuery总结
SubQuery天天用了,理论上总结一下.SubQuery 分三种 1.Noncorrelated 子查询 最普通的样式.
2.Correlated Subqueries 把父查询的列拉到子查询里面去,头一回cyt教我的时候理解了半天. 3.Inline View 也被当成最普通的样式用了.
然后Noncorrelated 子查询又有三种情况
1.返回一行一列 where price < (select max(price) from goods )
2.返回多行一列 where price>= ALL (select price from goods where type=2) or where NOT price< ANY(select price from goods where type=2) 最常用的IN其实就是=ANY()
3.返回多行多列 一次返回多列当然就节省了查询时间
UPDATE monthly_orders SET (tot_orders, max_order_amt) = (SELECT COUNT(*),
MAX(sale_price) FROM cust_order) DELETE FROM line_item WHERE (order_nbr, part_nbr) IN (SELECT order_nbr, part_nbr FROM cust_order c) ======================================== /*--------理解grouping sets select a, b, c, sum( d ) from t group by grouping sets ( a, b, c ) 等效于 select * from (
select a, null, null, sum( d ) from t group by a union all
select null, b, null, sum( d ) from t group by b union all
select null, null, c, sum( d ) from t group by c ) */ 二﹑
开窗函数的的理解:
开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化,举例如下:
over(order by salary) 按照salary排序进行累计,order by是个默认的开窗函数 over(partition by deptno)按照部门分区
over(order by salary range between 50 preceding and 150 following)
每行对应的数据窗口是之前行幅度值不超过50,之后行幅度值不超过150 over(order by salary rows between 50 preceding and 150 following) 每行对应的数据窗口是之前50行,之后150行
over(order by salary rows between unbounded preceding and unbounded following) 每行对应的数据窗口是从第一行到最后一行,等效:
over(order by salary range between unbounded preceding and unbounded following) 主要参考资料:《expert one-on-one》 Tom Kyte 《Oracle9i SQL Reference》第6章
AVG
功能描述:用于计算一个组和数据窗口内表达式的帄均值。
SAMPLE:下面的例子中列c_mavg计算员工表中每个员工的帄均薪水报告,该帄均值由当前员工和与之具有相同经理的前一个和后一个三者的帄均数得来; SELECT manager_id, last_name, hire_date, salary,
AVG(salary) OVER (PARTITION BY manager_id ORDER BY hire_date ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS c_mavg FROM employees;
MANAGER_ID LAST_NAME HIRE_DATE SALARY C_MAVG ---------- ------------------------- --------- ---------- ---------- 100 Kochhar 21-SEP-89 17000 17000 100 De Haan 13-JAN-93 17000 15000 100 Raphaely 07-DEC-94 11000 11966.6667 100 Kaufling 01-MAY-95 7900 10633.3333 100 Hartstein 17-FEB-96 13000 9633.33333 100 Weiss 18-JUL-96 8000 11666.6667 100 Russell 01-OCT-96 14000 11833.3333 CORR
功能描述:返回一对表达式的相关系数,它是如下的缩写: COVAR_POP(expr1,expr2)/STDDEV_POP(expr1)*STDDEV_POP(expr2))
从统计上讲,相关性是变量之间关联的强度,变量之间的关联意味着在某种程度 上一个变量的值可由其它的值进行预测。通过返回一个-1~1之间的一个数, 相关 系数给出了关联的强度,0表示不相关。
SAMPLE:下例返回1998年月销售收入和月单位销售的关系的累积系数(本例在SH用户下运行) SELECT t.calendar_month_number,
CORR (SUM(s.amount_sold), SUM(s.quantity_sold))
OVER (ORDER BY t.calendar_month_number) as CUM_CORR FROM sales s, times t
WHERE s.time_id = t.time_id AND calendar_year = 1998 GROUP BY t.calendar_month_number ORDER BY t.calendar_month_number; CALENDAR_MONTH_NUMBER CUM_CORR --------------------- ---------- 1
2 1
3 .994309382 4 .852040875 5 .846652204 6 .871250628 7 .910029803 8 .917556399 9 .920154356 10 .86720251 11 .844864765 12 .903542662
COVAR_POP
功能描述:返回一对表达式的总体协方差。
SAMPLE:下例CUM_COVP返回定价和最小产品价格的累积总体协方差 SELECT product_id, supplier_id, COVAR_POP(list_price, min_price)
OVER (ORDER BY product_id, supplier_id) AS CUM_COVP, COVAR_SAMP(list_price, min_price)
OVER (ORDER BY product_id, supplier_id) AS CUM_COVS FROM product_information p WHERE category_id = 29
ORDER BY product_id, supplier_id;
PRODUCT_ID SUPPLIER_ID CUM_COVP CUM_COVS ---------- ----------- ---------- ---------- 1774 103088 0
1775 103087 1473.25 2946.5 1794 103096 1702.77778 2554.16667 1825 103093 1926.25 2568.33333 2004 103086 1591.4 1989.25 2005 103086 1512.5 1815
2416 103088 1475.97959 1721.97619 . .
COVAR_SAMP
功能描述:返回一对表达式的样本协方差
SAMPLE:下例CUM_COVS返回定价和最小产品价格的累积样本协方差 SELECT product_id, supplier_id, COVAR_POP(list_price, min_price)
OVER (ORDER BY product_id, supplier_id) AS CUM_COVP, COVAR_SAMP(list_price, min_price)
OVER (ORDER BY product_id, supplier_id) AS CUM_COVS FROM product_information p WHERE category_id = 29
ORDER BY product_id, supplier_id;
PRODUCT_ID SUPPLIER_ID CUM_COVP CUM_COVS ---------- ----------- ---------- ---------- 1774 103088 0
1775 103087 1473.25 2946.5 1794 103096 1702.77778 2554.16667 1825 103093 1926.25 2568.33333 2004 103086 1591.4 1989.25 2005 103086 1512.5 1815
2416 103088 1475.97959 1721.97619 . .
COUNT
功 能描述:对一组内发生的事情进行累积计数,如果指定*或一些非空常数,count将对所有行计数,如果指定一个表达式,count返回表达式非空赋值的计 数,当有相同值出现时,这些相等的值都会被纳入被计算的值;可以使用DISTINCT来记录去掉一组中完全相同的数据后出现的行数。
SAMPLE:下面例子中计算每个员工在按薪水排序中当前行附近薪水在[n-50,n+150]之间的行数,n表示当前行的薪水
例如,Philtanker的薪水2200,排在他之前的行中薪水大于等于2200-50的有1行,排在他之后的行中薪水小于等于2200+150的行没有,所以count计数值cnt3为2(包括自己当前行);cnt2值相当于小于等于当前行的SALARY值的所有行数
SELECT last_name, salary, COUNT(*) OVER () AS cnt1, COUNT(*) OVER (ORDER BY salary) AS cnt2,
COUNT(*) OVER (ORDER BY salary RANGE BETWEEN 50 PRECEDING AND 150 FOLLOWING) AS cnt3 FROM employees; LAST_NAME SALARY CNT1 CNT2 CNT3
------------------------- ---------- ---------- ---------- ---------- Olson 2100 107 1 3 Markle 2200 107 3 2 Philtanker 2200 107 3 2 Landry 2400 107 5 8 Gee 2400 107 5 8 Colmenares 2500 107 11 10 Patel 2500 107 11 10 . .
CUME_DIST
功能描述:计算一行在组中的相对位置,CUME_DIST总是返回大于0、小于或等于1的数,该数表示该行在N行中的位置。例如,在一个3行的组中,返回的累计分布值为1/3、2/3、3/3 SAMPLE:下例中计算每个工种的员工按薪水排序依次累积出现的分布百分比 SELECT job_id, last_name, salary, CUME_DIST()
OVER (PARTITION BY job_id ORDER BY salary) AS cume_dist FROM employees WHERE job_id LIKE 'PU%'; JOB_ID LAST_NAME SALARY CUME_DIST ---------- ------------------------- ---------- ---------- PU_CLERK Colmenares 2500 .2 PU_CLERK Himuro 2600 .4 PU_CLERK Tobias 2800 .6 PU_CLERK Baida 2900 .8 PU_CLERK Khoo 3100 1 PU_MAN Raphaely 11000 1
DENSE_RANK
功 能描述:根据ORDER BY子句中表达式的值,从查询返回的每一行,计算它们与其它行的相对位置。组内的数据按ORDER BY子句排序,然后给每一行赋一个号,从而形成一个序列,该
序列从1开始,往后累加。每次ORDER BY表达式的值发生变化时,该序列也随之增加。有同样值的行得到同样的数字序号(认为null时相等的)。密集的序列返回的时没有间隔的数 SAMPLE:下例中计算每个员工按部门分区再按薪水排序,依次出现的序列号(注意与RANK函数的区别)
SELECT d.department_id , e.last_name, e.salary, DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary) as drank FROM employees e, departments d WHERE e.department_id = d.department_id AND d.department_id IN ('60', '90');
DEPARTMENT_ID LAST_NAME SALARY DRANK ------------- ------------------------- ---------- ---------- 60 Lorentz 4200 1 60 Austin 4800 2 60 Pataballa 4800 2 60 Ernst 6000 3 60 Hunold 9000 4 90 Kochhar 17000 1 90 De Haan 17000 1 90 King 24000 2
FIRST
功能描述:从DENSE_RANK返回的集合中取出排在最前面的一个值的行(可能多行,因为值可能相等),因此完整的语法需要在开始处加上一个集合函数以从中取出记录
SAMPLE: 下面例子中DENSE_RANK按部门分区,再按佣金commission_pct排序,FIRST取出佣金最低的对应的所有行,然后前面的MAX函数从 这个集合中取出薪水最低的值;LAST取出佣金最高的对应的所有行,然后前面的MIN函数从这个集合中取出薪水最高的值 SELECT last_name, department_id, salary,
MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY commission_pct) OVER (PARTITION BY department_id) \"Worst\
MAX(salary) KEEP (DENSE_RANK LAST ORDER BY commission_pct) OVER (PARTITION BY department_id) \"Best\" FROM employees
WHERE department_id in (20,80) ORDER BY department_id, salary;
LAST_NAME DEPARTMENT_ID SALARY Worst Best ------------------------- ------------- ---------- ---------- ---------- Fay 20 6000 6000 13000 Hartstein 20 13000 6000 13000 Kumar 80 6100 6100 14000 Banda 80 6200 6100 14000 Johnson 80 6200 6100 14000 Ande 80 6400 6100 14000 Lee 80 6800 6100 14000 Tuvault 80 7000 6100 14000 Sewall 80 7000 6100 14000 Marvins 80 7200 6100 14000 Bates 80 7300 6100 14000 . . .
FIRST_VALUE
功能描述:返回组中数据窗口的第一个值。
SAMPLE:下面例子计算按部门分区按薪水排序的数据窗口的第一个值对应的名字,如果薪水的第一个值有多个,则从多个对应的名字中取缺省排序的第一个名字 SELECT department_id, last_name, salary, FIRST_VALUE(last_name) OVER (PARTITION BY department_id ORDER BY salary ASC ) AS lowest_sal FROM employees
WHERE department_id in(20,30);
DEPARTMENT_ID LAST_NAME SALARY LOWEST_SAL ------------- ------------------------- ---------- -------------- 20 Fay 6000 Fay 20 Hartstein 13000 Fay 30 Colmenares 2500 Colmenares 30 Himuro 2600 Colmenares 30 Tobias 2800 Colmenares 30 Baida 2900 Colmenares 30 Khoo 3100 Colmenares 30 Raphaely 11000 Colmenares
LAG
功 能描述:可以访问结果集中的其它行而不用进行自连接。它允许去处理游标,就好像游标是一个数组一样。在给定组中可参考当前行之前的行,这样就可以从组中与 当前行一起选择以前的行。Offset是一个正整数,其默认值为1,若索引超出窗口的范围,就返回默认值(默认返回的是组中第一行),其相反的函数是 LEAD
SAMPLE:下面的例子中列prev_sal返回按hire_date排序的前1行的salary值 SELECT last_name, hire_date, salary,
LAG(salary, 1, 0) OVER (ORDER BY hire_date) AS prev_sal FROM employees
WHERE job_id = 'PU_CLERK';
LAST_NAME HIRE_DATE SALARY PREV_SAL ------------------------- ---------- ---------- ---------- Khoo 18-5月 -95 3100 0 Tobias 24-7月 -97 2800 3100 Baida 24-12月-97 2900 2800 Himuro 15-11月-98 2600 2900 Colmenares 10-8月 -99 2500 2600
LAST
功能描述:从DENSE_RANK返回的集合中取出排在最后面的一个值的行(可能多行,因为值可能相等),因此完整的语法需要在开始处加上一个集合函数以从中取出记录
SAMPLE: 下面例子中DENSE_RANK按部门分区,再按佣金commission_pct排序,FIRST取出佣金最低的对应的所有行,然后前面的MAX函数从 这个集合中取出薪水最低的值;LAST取出佣金最高的对应的所有行,然后前面的MIN函数从这个集合中取出薪水最高的值 SELECT last_name, department_id, salary,
MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY commission_pct) OVER (PARTITION BY department_id) \"Worst\
MAX(salary) KEEP (DENSE_RANK LAST ORDER BY commission_pct) OVER (PARTITION BY department_id) \"Best\" FROM employees
WHERE department_id in (20,80) ORDER BY department_id, salary;
LAST_NAME DEPARTMENT_ID SALARY Worst Best ------------------------- ------------- ---------- ---------- ----------
Fay 20 6000 6000 13000 Hartstein 20 13000 6000 13000 Kumar 80 6100 6100 14000 Banda 80 6200 6100 14000 Johnson 80 6200 6100 14000 Ande 80 6400 6100 14000 Lee 80 6800 6100 14000 Tuvault 80 7000 6100 14000 Sewall 80 7000 6100 14000 Marvins 80 7200 6100 14000 Bates 80 7300 6100 14000 .
LAST_VALUE
功能描述:返回组中数据窗口的最后一个值。
SAMPLE:下面例子计算按部门分区按薪水排序的数据窗口的最后一个值对应的名字,如果薪水的最后一个值有多个,则从多个对应的名字中取缺省排序的最后一个名字 SELECT department_id, last_name, salary, LAST_VALUE(last_name) OVER(PARTITION BY department_id ORDER BY salary) AS highest_sal FROM employees
WHERE department_id in(20,30);
DEPARTMENT_ID LAST_NAME SALARY HIGHEST_SAL ------------- ------------------------- ---------- ------------ 20 Fay 6000 Fay
20 Hartstein 13000 Hartstein 30 Colmenares 2500 Colmenares 30 Himuro 2600 Himuro 30 Tobias 2800 Tobias 30 Baida 2900 Baida 30 Khoo 3100 Khoo 30 Raphaely 11000 Raphaely
LEAD
功能描述:LEAD与LAG相反,LEAD可以访问组中当前行之后的行。Offset是一个正整数,其默认值为1,若索引超出窗口的范围,就返回默认值(默认返回的是组中第一行) SAMPLE:下面的例子中每行的\"NextHired\"返回按hire_date排序的下一行的hire_date值
SELECT last_name, hire_date,
LEAD(hire_date, 1) OVER (ORDER BY hire_date) AS \"NextHired\" FROM employees WHERE department_id = 30; LAST_NAME HIRE_DATE NextHired ------------------------- --------- --------- Raphaely 07-DEC-94 18-MAY-95 Khoo 18-MAY-95 24-JUL-97 Tobias 24-JUL-97 24-DEC-97 Baida 24-DEC-97 15-NOV-98 Himuro 15-NOV-98 10-AUG-99 Colmenares 10-AUG-99
MAX
功能描述:在一个组中的数据窗口中查找表达式的最大值。 SAMPLE:下面例子中dept_max返回当前行所在部门的最大薪水值 SELECT department_id, last_name, salary,
MAX(salary) OVER (PARTITION BY department_id) AS dept_max FROM employees WHERE department_id in (10,20,30); DEPARTMENT_ID LAST_NAME SALARY DEPT_MAX ------------- ------------------------- ---------- ---------- 10 Whalen 4400 4400 20 Hartstein 13000 13000 20 Fay 6000 13000 30 Raphaely 11000 11000 30 Khoo 3100 11000 30 Baida 2900 11000 30 Tobias 2800 11000 30 Himuro 2600 11000 30 Colmenares 2500 11000
MIN
功能描述:在一个组中的数据窗口中查找表达式的最小值。 SAMPLE:下面例子中dept_min返回当前行所在部门的最小薪水值
SELECT department_id, last_name, salary,
MIN(salary) OVER (PARTITION BY department_id) AS dept_min FROM employees WHERE department_id in (10,20,30); DEPARTMENT_ID LAST_NAME SALARY DEPT_MIN ------------- ------------------------- ---------- ---------- 10 Whalen 4400 4400 20 Hartstein 13000 6000 20 Fay 6000 6000 30 Raphaely 11000 2500 30 Khoo 3100 2500 30 Baida 2900 2500 30 Tobias 2800 2500 30 Himuro 2600 2500 30 Colmenares 2500 2500
NTILE
功 能描述:将一个组分为\"表达式\"的散列表示,例如,如果表达式=4,则给组中的每一行分配一个数(从1到4),如果组中有20行,则给前5行分配1,给下 5行分配2等等。如果组的基数不能由表达式值帄均分开,则对这些行进行分配时,组中就没有任何percentile的行数比其它percentile的行 数超过一行,最低的percentile是那些拥有额外行的percentile。例如,若表达式=4,行数=21,则percentile=1的有5 行,percentile=2的有5行等等。 SAMPLE:下例中把6行数据分为4份 SELECT last_name, salary,
NTILE(4) OVER (ORDER BY salary DESC) AS quartile FROM employees WHERE department_id = 100; LAST_NAME SALARY QUARTILE ------------------------- ---------- ---------- Greenberg 12000 1 Faviet 9000 1 Chen 8200 2 Urman 7800 2 Sciarra 7700 3 Popp 6900 4
PERCENT_RANK
功能描述:和CUME_DIST(累积分配)函数类似,对于一个组中给定的行来说,在计算那行的序号时,先减1,然后除以n-1(n为组中所有的行数)。该函数总是返回0~1(包括1)之间的数。
SAMPLE:下例中如果Khoo的salary为2900,则pr值为0.6,因为RANK函数对于等值的返回序列值是一样的
SELECT department_id, last_name, salary, PERCENT_RANK()
OVER (PARTITION BY department_id ORDER BY salary) AS pr FROM employees
WHERE department_id < 50 ORDER BY department_id,salary;
DEPARTMENT_ID LAST_NAME SALARY PR ------------- ------------------------- ---------- ---------- 10 Whalen 4400 0 20 Fay 6000 0 20 Hartstein 13000 1 30 Colmenares 2500 0 30 Himuro 2600 0.2 30 Tobias 2800 0.4 30 Baida 2900 0.6 30 Khoo 3100 0.8 30 Raphaely 11000 1 40 Mavris 6500 0
PERCENTILE_CONT
功能描述:返回一个与输入的分布百分比值相对应的数据值,分布百分比的计算方法见函数PERCENT_RANK,如果没有正好对应的数据值,就通过下面算法来得到值: RN = 1+ (P*(N-1)) 其中P是输入的分布百分比值,N是组内的行数 CRN = CEIL(RN) FRN = FLOOR(RN) if (CRN = FRN = RN) then (value of expression from row at RN) else
(CRN - RN) * (value of expression for row at FRN) + (RN - FRN) * (value of expression for row at CRN)
注意:本函数与PERCENTILE_DISC的区别在找不到对应的分布值时返回的替代值的计算方法不同
SAMPLE:在下例中,对于部门60的Percentile_Cont值计算如下: P=0.7 N=5 RN =1+ (P*(N-1)=1+(0.7*(5-1))=3.8 CRN = CEIL(3.8)=4 FRN = FLOOR(3.8)=3
(4 - 3.8)* 4800 + (3.8 - 3) * 6000 = 5760 SELECT last_name, salary, department_id,
PERCENTILE_CONT(0.7) WITHIN GROUP (ORDER BY salary) OVER (PARTITION BY department_id) \"Percentile_Cont\PERCENT_RANK()
OVER (PARTITION BY department_id ORDER BY salary) \"Percent_Rank\" FROM employees WHERE department_id IN (30, 60);
LAST_NAME SALARY DEPARTMENT_ID Percentile_Cont Percent_Rank ------------------------- ---------- ------------- --------------- ------------ Colmenares 2500 30 3000 0 Himuro 2600 30 3000 0.2 Tobias 2800 30 3000 0.4 Baida 2900 30 3000 0.6 Khoo 3100 30 3000 0.8 Raphaely 11000 30 3000 1 Lorentz 4200 60 5760 0 Austin 4800 60 5760 0.25 Pataballa 4800 60 5760 0.25 Ernst 6000 60 5760 0.75 Hunold 9000 60 5760 1
PERCENTILE_DISC
功能描述:返回一个与输入的分布百分比值相对应的数据值,分布百分比的计算方法见函数CUME_DIST,如果没有正好对应的数据值,就取大于该分布值的下一个值。
注意:本函数与PERCENTILE_CONT的区别在找不到对应的分布值时返回的替代值的计算方法不同
SAMPLE:下例中0.7的分布值在部门30中没有对应的Cume_Dist值,所以就取下一个分布值0.83333333所对应的SALARY来替代 SELECT last_name, salary, department_id,
PERCENTILE_DISC(0.7) WITHIN GROUP (ORDER BY salary ) OVER (PARTITION BY department_id) \"Percentile_Disc\
CUME_DIST() OVER (PARTITION BY department_id ORDER BY salary) \"Cume_Dist\"
FROM employees
WHERE department_id in (30, 60);
LAST_NAME SALARY DEPARTMENT_ID Percentile_Disc Cume_Dist ------------------------- ---------- ------------- --------------- ---------- Colmenares 2500 30 3100 .166666667 Himuro 2600 30 3100 .333333333 Tobias 2800 30 3100 .5 Baida 2900 30 3100 .666666667 Khoo 3100 30 3100 .833333333 Raphaely 11000 30 3100 1 Lorentz 4200 60 6000 .2 Austin 4800 60 6000 .6 Pataballa 4800 60 6000 .6 Ernst 6000 60 6000 .8 Hunold 9000 60 6000 1
RANK
功 能描述:根据ORDER BY子句中表达式的值,从查询返回的每一行,计算它们与其它行的相对位置。组内的数据按ORDER BY子句排序,然后给每一行赋一个号,从而形成一个序列,该序列从1开始,往后累加。每次ORDER BY表达式的值发生变化时,该序列也随之增加。有同样值的行得到同样的数字序号(认为null时相等的)。然而,如果两行的确得到同样的排序,则序数将随 后跳跃。若两行序数为1,则没有序数2,序列将给组中的下一行分配值3,DENSE_RANK则没有任何跳跃。
SAMPLE:下例中计算每个员工按部门分区再按薪水排序,依次出现的序列号(注意与DENSE_RANK函数的区别)
SELECT d.department_id , e.last_name, e.salary, RANK()
OVER (PARTITION BY e.department_id ORDER BY e.salary) as drank FROM employees e, departments d WHERE e.department_id = d.department_id AND d.department_id IN ('60', '90');
DEPARTMENT_ID LAST_NAME SALARY DRANK ------------- ------------------------- ---------- ---------- 60 Lorentz 4200 1 60 Austin 4800 2 60 Pataballa 4800 2
60 Ernst 6000 4 60 Hunold 9000 5 90 Kochhar 17000 1 90 De Haan 17000 1 90 King 24000 3
RATIO_TO_REPORT
功能描述:该函数计算expression/(sum(expression))的值,它给出相对于总数的百分比,即当前行对sum(expression)的贡献。
SAMPLE:下例计算每个员工的工资占该类员工总工资的百分比 SELECT last_name, salary, RATIO_TO_REPORT(salary) OVER () AS rr FROM employees
WHERE job_id = 'PU_CLERK'; LAST_NAME SALARY RR ------------------------- ---------- ---------- Khoo 3100 .223021583 Baida 2900 .208633094 Tobias 2800 .201438849 Himuro 2600 .18705036 Colmenares 2500 .179856115
REGR_ (Linear Regression) Functions
功能描述:这些线性回归函数适合最小二乘法回归线,有9个不同的回归函数可使用。 REGR_SLOPE:返回斜率,等于COVAR_POP(expr1, expr2) / VAR_POP(expr2) REGR_INTERCEPT:返回回归线的y截距,等于 AVG(expr1) - REGR_SLOPE(expr1, expr2) * AVG(expr2) REGR_COUNT:返回用于填充回归线的非空数字对的数目 REGR_R2:返回回归线的决定系数,计算式为: If VAR_POP(expr2) = 0 then return NULL
If VAR_POP(expr1) = 0 and VAR_POP(expr2) != 0 then return 1 If VAR_POP(expr1) > 0 and VAR_POP(expr2 != 0 then return POWER(CORR(expr1,expr),2)
REGR_AVGX:计算回归线的自变量(expr2)的帄均值,去掉了空对(expr1, expr2)后,等于AVG(expr2) REGR_AVGY:计算回归线的应变量(expr1)的帄均值,去掉了空对(expr1, expr2)后,等于AVG(expr1) REGR_SXX: 返回值等于REGR_COUNT(expr1, expr2) * VAR_POP(expr2)
REGR_SYY: 返回值等于REGR_COUNT(expr1, expr2) * VAR_POP(expr1) REGR_SXY: 返回值等于REGR_COUNT(expr1, expr2) * COVAR_POP(expr1, expr2) (下面的例子都是在SH用户下完成的)
SAMPLE 1:下例计算1998年最后三个星期中两种产品(260和270)在周末的销售量中已开发票数量和总数量的累积斜率和回归线的截距
SELECT t.fiscal_month_number \"Month\ REGR_SLOPE(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_month_desc, t.day_number_in_month) AS CUM_SLOPE, REGR_INTERCEPT(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_month_desc, t.day_number_in_month) AS CUM_ICPT FROM sales s, times t WHERE s.time_id = t.time_id AND s.prod_id IN (270, 260) AND t.fiscal_year=1998
AND t.fiscal_week_number IN (50, 51, 52) AND t.day_number_in_week IN (6,7)
ORDER BY t.fiscal_month_desc, t.day_number_in_month; Month Day CUM_SLOPE CUM_ICPT ---------- ---------- ---------- ---------- 12 12 -68 1872 12 12 -68 1872
12 13 -20.244898 1254.36735 12 13 -20.244898 1254.36735 12 19 -18.826087 1287 12 20 62.4561404 125.28655 12 20 62.4561404 125.28655 12 20 62.4561404 125.28655 12 20 62.4561404 125.28655 12 26 67.2658228 58.9712313 12 26 67.2658228 58.9712313 12 27 37.5245541 284.958221 12 27 37.5245541 284.958221 12 27 37.5245541 284.958221
SAMPLE 2:下例计算1998年4月每天的累积交易数量
SELECT UNIQUE t.day_number_in_month, REGR_COUNT(s.amount_sold, s.quantity_sold)
OVER (PARTITION BY t.fiscal_month_number ORDER BY t.day_number_in_month) \"Regr_Count\" FROM sales s, times t WHERE s.time_id = t.time_id
AND t.fiscal_year = 1998 AND t.fiscal_month_number = 4; DAY_NUMBER_IN_MONTH Regr_Count ------------------- ---------- 1 825 2 1650 3 2475 4 3300 . . . 26 21450 30 22200
SAMPLE 3:下例计算1998年每月销售量中已开发票数量和总数量的累积回归线决定系数 SELECT t.fiscal_month_number,
REGR_R2(SUM(s.amount_sold), SUM(s.quantity_sold)) OVER (ORDER BY t.fiscal_month_number) \"Regr_R2\" FROM sales s, times t WHERE s.time_id = t.time_id AND t.fiscal_year = 1998 GROUP BY t.fiscal_month_number ORDER BY t.fiscal_month_number; FISCAL_MONTH_NUMBER Regr_R2 ------------------- ---------- 1 2 1
3 .927372984 4 .807019972 5 .932745567 6 .94682861
7 .965342011 8 .955768075 9 .959542618 10 .938618575 11 .880931415 12 .882769189
SAMPLE 4:下例计算1998年12月最后两周产品260的销售量中已开发票数量和总数量的累积帄均值
SELECT t.day_number_in_month,
REGR_AVGY(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_month_desc, t.day_number_in_month) \"Regr_AvgY\
REGR_AVGX(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_month_desc, t.day_number_in_month) \"Regr_AvgX\" FROM sales s, times t WHERE s.time_id = t.time_id AND s.prod_id = 260
AND t.fiscal_month_desc = '1998-12' AND t.fiscal_week_number IN (51, 52) ORDER BY t.day_number_in_month;
DAY_NUMBER_IN_MONTH Regr_AvgY Regr_AvgX ------------------- ---------- ---------- 14 882 24.5 14 882 24.5 15 801 22.25 15 801 22.25 16 777.6 21.6
18 642.857143 17.8571429 18 642.857143 17.8571429 20 589.5 16.375 21 544 15.1111111 22 592.363636 16.4545455 22 592.363636 16.4545455 24 553.846154 15.3846154 24 553.846154 15.3846154
26 522 14.5 27 578.4 16.0666667
SAMPLE 5:下例计算产品260和270在1998年2月周末销售量中已开发票数量和总数量的累积REGR_SXY, REGR_SXX, and REGR_SYY统计值 SELECT t.day_number_in_month, REGR_SXY(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_year, t.fiscal_month_desc) \"Regr_sxy\REGR_SYY(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_year, t.fiscal_month_desc) \"Regr_syy\REGR_SXX(s.amount_sold, s.quantity_sold)
OVER (ORDER BY t.fiscal_year, t.fiscal_month_desc) \"Regr_sxx\" FROM sales s, times t WHERE s.time_id = t.time_id AND prod_id IN (270, 260) AND t.fiscal_month_desc = '1998-02' AND t.day_number_in_week IN (6,7) ORDER BY t.day_number_in_month;
DAY_NUMBER_IN_MONTH Regr_sxy Regr_syy Regr_sxx ------------------- ---------- ---------- ---------- 1 18870.4 2116198.4 258.4 1 18870.4 2116198.4 258.4 1 18870.4 2116198.4 258.4 1 18870.4 2116198.4 258.4 7 18870.4 2116198.4 258.4 8 18870.4 2116198.4 258.4 14 18870.4 2116198.4 258.4 15 18870.4 2116198.4 258.4 21 18870.4 2116198.4 258.4 22 18870.4 2116198.4 258.4
ROW_NUMBER
功能描述:返回有序组中一行的偏移量,从而可用于按特定标准排序的行号。 SAMPLE:下例返回每个员工再在每个部门中按员工号排序后的顺序号 SELECT department_id, last_name, employee_id, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY employee_id) AS emp_id
FROM employees
WHERE department_id < 50;
DEPARTMENT_ID LAST_NAME EMPLOYEE_ID EMP_ID ------------- ------------------------- ----------- ---------- 10 Whalen 200 1 20 Hartstein 201 1 20 Fay 202 2 30 Raphaely 114 1 30 Khoo 115 2 30 Baida 116 3 30 Tobias 117 4 30 Himuro 118 5 30 Colmenares 119 6 40 Mavris 203 1
STDDEV
功能描述:计算当前行关于组的标准偏离。(Standard Deviation) SAMPLE:下例返回部门30按雇佣日期排序的薪水值的累积标准偏离 SELECT last_name, hire_date,salary,
STDDEV(salary) OVER (ORDER BY hire_date) \"StdDev\" FROM employees WHERE department_id = 30;
LAST_NAME HIRE_DATE SALARY StdDev ------------------------- ---------- ---------- ---------- Raphaely 07-12月-94 11000 0 Khoo 18-5月 -95 3100 5586.14357 Tobias 24-7月 -97 2800 4650.0896 Baida 24-12月-97 2900 4035.26125 Himuro 15-11月-98 2600 3649.2465 Colmenares 10-8月 -99 2500 3362.58829
STDDEV_POP
功能描述:该函数计算总体标准偏离,并返回总体变量的帄方根,其返回值与VAR_POP函数的帄方根相同。(Standard Deviation-Population)
SAMPLE:下例返回部门20、30、60的薪水值的总体标准偏差
SELECT department_id, last_name, salary,
STDDEV_POP(salary) OVER (PARTITION BY department_id) AS pop_std FROM employees
WHERE department_id in (20,30,60);
DEPARTMENT_ID LAST_NAME SALARY POP_STD ------------- ------------------------- ---------- ---------- 20 Hartstein 13000 3500 20 Fay 6000 3500
30 Raphaely 11000 3069.6091 30 Khoo 3100 3069.6091 30 Baida 2900 3069.6091 30 Colmenares 2500 3069.6091 30 Himuro 2600 3069.6091 30 Tobias 2800 3069.6091 60 Hunold 9000 1722.32401 60 Ernst 6000 1722.32401 60 Austin 4800 1722.32401 60 Pataballa 4800 1722.32401 60 Lorentz 4200 1722.32401
STDDEV_SAMP
功能描述: 该函数计算累积样本标准偏离,并返回总体变量的帄方根,其返回值与VAR_POP函数的帄方根相同。(Standard Deviation-Sample)
SAMPLE:下例返回部门20、30、60的薪水值的样本标准偏差 SELECT department_id, last_name, hire_date, salary, STDDEV_SAMP(salary) OVER
(PARTITION BY department_id ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum_sdev FROM employees
WHERE department_id in (20,30,60);
DEPARTMENT_ID LAST_NAME HIRE_DATE SALARY CUM_SDEV ------------- ------------------------- ---------- ---------- ---------- 20 Hartstein 17-2月 -96 13000 20 Fay 17-8月 -97 6000 4949.74747 30 Raphaely 07-12月-94 11000
30 Khoo 18-5月 -95 3100 5586.14357 30 Tobias 24-7月 -97 2800 4650.0896 30 Baida 24-12月-97 2900 4035.26125 30 Himuro 15-11月-98 2600 3649.2465 30 Colmenares 10-8月 -99 2500 3362.58829 60 Hunold 03-1月 -90 9000 60 Ernst 21-5月 -91 6000 2121.32034 60 Austin 25-6月 -97 4800 2163.33077 60 Pataballa 05-2月 -98 4800 1982.42276 60 Lorentz 07-2月 -99 4200 1925.61678
SUM
功能描述:该函数计算组中表达式的累积和。 SAMPLE:下例计算同一经理下员工的薪水累积值 SELECT manager_id, last_name, salary,
SUM (salary) OVER (PARTITION BY manager_id ORDER BY salary RANGE UNBOUNDED PRECEDING) l_csum FROM employees
WHERE manager_id in (101,103,108);
MANAGER_ID LAST_NAME SALARY L_CSUM ---------- ------------------------- ---------- ---------- 101 Whalen 4400 4400 101 Mavris 6500 10900 101 Baer 10000 20900 101 Greenberg 12000 44900 101 Higgins 12000 44900 103 Lorentz 4200 4200 103 Austin 4800 13800 103 Pataballa 4800 13800 103 Ernst 6000 19800 108 Popp 6900 6900 108 Sciarra 7700 14600 108 Urman 7800 22400 108 Chen 8200 30600 108 Faviet 9000 39600
VAR_POP
功能描述:(Variance Population)该函数返回非空集合的总体变量(忽略null),VAR_POP进行如下计算:
(SUM(expr2) - SUM(expr)2 / COUNT(expr)) / COUNT(expr)
SAMPLE:下例计算1998年每月销售的累积总体和样本变量(本例在SH用户下运行) SELECT t.calendar_month_desc, VAR_POP(SUM(s.amount_sold))
OVER (ORDER BY t.calendar_month_desc) \"Var_Pop\VAR_SAMP(SUM(s.amount_sold))
OVER (ORDER BY t.calendar_month_desc) \"Var_Samp\" FROM sales s, times t
WHERE s.time_id = t.time_id AND t.calendar_year = 1998 GROUP BY t.calendar_month_desc; CALENDAR Var_Pop Var_Samp -------- ---------- ---------- 1998-01 0
1998-02 6.1321E+11 1.2264E+12 1998-03 4.7058E+11 7.0587E+11 1998-04 4.6929E+11 6.2572E+11 1998-05 1.5524E+12 1.9405E+12 1998-06 2.3711E+12 2.8453E+12 1998-07 3.7464E+12 4.3708E+12 1998-08 3.7852E+12 4.3260E+12 1998-09 3.5753E+12 4.0222E+12 1998-10 3.4343E+12 3.8159E+12 1998-11 3.4245E+12 3.7669E+12 1998-12 4.8937E+12 5.3386E+12
VAR_SAMP
功能描述:(Variance Sample)该函数返回非空集合的样本变量(忽略null),VAR_POP进行如下计算:
(SUM(expr*expr)-SUM(expr)*SUM(expr)/COUNT(expr))/(COUNT(expr)-1) SAMPLE:下例计算1998年每月销售的累积总体和样本变量
SELECT t.calendar_month_desc, VAR_POP(SUM(s.amount_sold))
OVER (ORDER BY t.calendar_month_desc) \"Var_Pop\VAR_SAMP(SUM(s.amount_sold))
OVER (ORDER BY t.calendar_month_desc) \"Var_Samp\" FROM sales s, times t
WHERE s.time_id = t.time_id AND t.calendar_year = 1998 GROUP BY t.calendar_month_desc; CALENDAR Var_Pop Var_Samp -------- ---------- ---------- 1998-01 0
1998-02 6.1321E+11 1.2264E+12 1998-03 4.7058E+11 7.0587E+11 1998-04 4.6929E+11 6.2572E+11 1998-05 1.5524E+12 1.9405E+12 1998-06 2.3711E+12 2.8453E+12 1998-07 3.7464E+12 4.3708E+12 1998-08 3.7852E+12 4.3260E+12 1998-09 3.5753E+12 4.0222E+12 1998-10 3.4343E+12 3.8159E+12 1998-11 3.4245E+12 3.7669E+12 1998-12 4.8937E+12 5.3386E+12
VARIANCE
功能描述:该函数返回表达式的变量,Oracle计算该变量如下: 如果表达式中行数为1,则返回0
如果表达式中行数大于1,则返回VAR_SAMP
SAMPLE:下例返回部门30按雇佣日期排序的薪水值的累积变化 SELECT last_name, salary, VARIANCE(salary) OVER (ORDER BY hire_date) \"Variance\" FROM employees WHERE department_id = 30; LAST_NAME SALARY Variance ------------------------- ---------- ---------- Raphaely 11000 0
Khoo 3100 31205000 Tobias 2800 21623333.3 Baida 2900 16283333.3 Himuro 2600 13317000 Colmenares 2500 11307000
=====================================
三﹑ORACLE查询树型关系(connect by prior start with)
connect by prior start with 经常会被用到一个表中存在递归关系的时候。比如我们经常会将一个比较复杂的目录树存储到一个表中。或者将一些部门存储到一个表中,而这些部门互相有隶属关系。这个时候你就会用到connect by prior start with。 典型的使用方法就是:
select * from table connect by prior cur_id=parent_id start with cur_id=??? 例如: a b 1 0 2 1 3 1 4 2 5 3
如果想查找a=2及其下面的所有数据,则: select * from table connect by prior a=b start with a=2 a b 2 1 4 2 例如1﹕
看成兩個集合 START WITH 為條件 select * from dv_boms
WHERE artic_code = '315066-001-00' AND PART_NO='00051' START WITH artic_code = '315066-001-00' and bom_ver =1 AND ITEM_NO = 60 and item_no_pa is null CONNECT BY
//以下條件為父主鍵=子主鍵
PRIOR artic_code = artic_code and PRIOR bom_ver = bom_ver and PRIOR item_no = item_no_pa;
SELECT PART_NO,ITEM_NO,ITEM_NO_PA FROM DV_BOMS
WHERE Artic_code = '315066-001-00' and bom_ver =1; AND PART_NO='00051'; 從那項開始找到底部關系圖 select * from dv_boms
START WITH artic_code = '315066-001-00' and bom_ver =1 and item_no_pa ='60' CONNECT BY
PRIOR artic_code = artic_code and PRIOR bom_ver = bom_ver AND PRIOR ITEM_NO=ITEM_NO_PA; 例如2﹕
select distinct artic_code,(case when level = 1 then part_no when level = 2 then ' '||part_no when level = 3 then ' '||part_no end ) part_no
,item_no,item_no_pa from dv_boms //where artic_code = '313324-002-00' connect by prior artic_code = artic_code and prior bom_ver = bom_ver and prior item_no <> item_no and prior item_no = item_no_pa
start with item_no = 60 and artic_code = '315066-001-00' ORDER BY part_no desc 例3﹕
SELECT lpad(' ',3 * level - 2) ,PART_ID ,level FROM BOM_DETAIL_NEW
WHERE PRODUCT_ID = :ls_proc_id
START WITH PARENT_PART_ID IS NULL
CONNECT BY PRIOR PART_ID = PARENT_PART_ID; 例3﹕
Oracle中使用connect by 来实现树状查询,其中可以使用level这个伪列来实现分层查询。 具体使用如下:
一张表menu记录菜单的层级情况。表结构如下: menu_id number, parent_id number, menu_name nvarchar2(20)
首先select * from menu connect by menu_id = parent_id start with menu_id = 1;
这样可以看到Oracle以树状结构产生结果。某些时候如果我只想要第2层的菜单项如何做呢。 select * from
(select level,menu_id,parent_id,menu_name from menu
conncet by menu_id = parent_id start with menu_id = 1 )
where level = 2
这样就可以得到层级为第二层的菜单项。这样的SQL写法同样适合rownum伪列,Oracle中实现TOP n查询就是如此写的。
二﹑1.查询语句的使用
使用 select语句和子查询(subquery)可以从一个或多个表,视图,实体试图中返回数据.
1.1相关子查询
可以将子查询(as subquery)或in或exists当成where的一个条件的一部分,这样的查询称为子查询
.where中可以包含一个select语句的子查询
.where中可以包含in,exists语句
.最多可以嵌套16层
.层次过多会影响性能
[例]简单子查询实例
查询是否有的专家既以研究所的名义来申请基金项目,又以大学系为单位申请项目
(按规定只能以一个单位来申请)
SQL> create table univ_subject
2 (
3 name varchar2(12) not null, 4 per_id number not null, 5 dept_name varchar2(20) 6 );
SQL> insert into univ_subject values('gaoqianjing',1001,'信息工程系'); SQL> insert into univ_subject values('wangbing',1002,'物理系'); SQL> insert into univ_subject values('liming',1003,'化学系'); ===============
SQL> create table colle_subject 2 (
3 colle_name varchar2(20), 4 per_id number 5 );
SQL> insert into colle_subject values('电子研究所',1001); SQL> insert into colle_subject values('物理研究所',1005); ================
SQL> select name,per_id,dept_name from univ_subject where per_id in 2 (select per_id from colle_subject);
NAME PER_ID DEPT_NAME ------------ --------- -------------------- gaoqianjing 1001 信息工程系
1.2外连接
[例]外连接实例
招生中所有学生的信息放在students表中,而部分有特长的学生在另一个表中stuent_skill中同样有该学生的信息。现在要全部列出所有学生,如果某个学生在表student_skill中就有其特长信息,并显示特长信息,如果某个学生没有特长就显示特长问空.
SQL> create table students 2 (
3 st_id varchar2(20), 4 name varchar2(10), 5 age number(2), 6 tol_score number(3)
7 ) ;
SQL> insert into students values('973231','wangbindu',22,501); SQL> insert into students values('973232','zhuzhijing',21,538); SQL> insert into students values('973233','gaojing',21,576); ===================
SQL> create table student_skill 2 (
3 st_id varchar2(20), 4 skill varchar2(20) 5 );
SQL> insert into student_skill values('973231','篮球'); SQL> insert into student_skill(st_id) values('973232'); SQL> insert into student_skill values('973233','足球'); ===================
SQL> select a.* , b.skill from students a,student_skill b where a.st_id=b.st_id(+) order by a.st_id;
ST_ID NAME AGE TOL_SCORE SKILL -------------------- ---------- --------- --------- ----------
973231 wangbindu 22 501 篮球 973232 zhuzhijing 21 538
973233 gaojing 21 576 足球
1.3自我连接
自我连接是在同一个表或视图内进行条件连接.
[例]自我连接实例
查询每个雇员的名字和该雇员的经理的名字:
SQL> select e1.ename||' work for '||e2.ename \"Employees and their Managers\"
2 from scott.emp e1,scott.emp e2 where e1.mgr=e2.empno;
Employees and their Managers
------------------------------------------------- SMITH work for FORD ALLEN work for BLAKE WARD work for BLAKE JONES work for KING MARTIN work for BLAKE BLAKE work for KING CLARK work for KING SCOTT work for JONES TURNER work for BLAKE ADAMS work for SCOTT JAMES work for BLAKE FORD work for JONES MILLER work for CLARK
1.4UNION , INTERSECT及 MINUS
UNION: 可以将两个以上的表的相类似的查询结果放在一起 (union all则表示返回所有的行)
具体语法: select ... union[all] select... ==========
INTERSECT: 返回两个表中相同的信息 具体语法: select ... intersect select... ==========
MINUS : 返回一个表中出现的信息 具体语法: select ... minus
select...
[例1]UNION操作实例
SQL> select st_id from students 2 union
3 select st_id from student_skill; ST_ID -------------------- 973231 973232 973233
[例2]INTERSECT操作实例
列出有特长的学生的学号
SQL> select st_id from students 2 intersect
3 select st_id from student_skill; ST_ID -------------------- 973231 973233
[例3]MINUS操作实例
列出没有特长学生的学号
select st_id from students minus
select st_id from student_skill; ST_ID -------------------- 973232
2.创建复杂的视图
许多应用系统有统计等功能,建议最好把这些复杂语句写成视图.下面是几个常用的视图.
2.1分组视图
[例1]简单的分组视图
SQL> create or replace view dept_tot as
2 select a.dname dept,sum(b.sal) total_sal from scott.dept a,scott.emp b 3 where a.deptno=b.deptno group by a.dname;
查看已建立。
SQL> select * from dept_tot;
DEPT TOTAL_SAL -------------- --------- ACCOUNTING 8750 RESEARCH 10875 SALES 9400
[例2]带复杂函数视图
SQL> create or replace view itemtot as
2 select persion,sum(amount) itemtot from ledger 3 where actiondate between
4 to_date('01-MAR-1901','dd-mon-yyyy') and 5 to_date('31-MAR-1901','dd-mon-yyyy') 6 and action in('bought','raid') group by persion;
2.2合计视图
[例]合计函数视图实例
SQL> create or replace view emp_no1 as
2 select deptno,sum(sal) 工资和,sum(comm) 总和 3 from scott.emp group by deptno; SQL> select * from emp_no1; DEPTNO 工资和 总和 -------- --------- --------- 10 8750
20 10875
30 9400 2200
2.3组合视图
[例]带组合函数的视图
SQL> create or replace view byitem as
2 select l.persion persion.item, amount, 100*amount/item bypersion,100*amount/total bytotal 3 from ledgent l,itemtotal i,total where l.persion=i.persion where l.persion=i.persion 4 and actiondate between
5 to_date('01-MAR-1901','dd-mon-yyyy') and 6 to_date('31-MAR-1901','dd-mon-yyyy') 7 and action in('bought','raid') ; 3.家族树 语法:
select column from table_name start with column=value connect by prior 父主键=子主键
3.1排除单一性和分枝
以ORACLE中的EMP表为例
[例]从顶到底列出各雇员的信息
SQL> select lpad(' ',4*(level-1))||ename name,empno,mgr from emp start with mgr is null 2 connect by prior empno=mgr;
NAME EMPNO MGR --------- --------- --------- KING 7839
JONES 7566 7839 SCOTT 7788 7566 ADAMS 7876 7788
3.2遍历至根
[例1]现在要从某个雇员开始向他的上级列出该雇员的层次结构lpad
SQL> col ename for a30;
SQL> select lpad(' ',4*(level-1))||ename ename,mgr,empno from scott.emp 2 start with mgr=7788 connect by prior mgr=empno;
ENAME MGR EMPNO -------------------------- --------- ---------
ADAMS 7788 7876 SCOTT 7566 7788 JONES 7839 7566 KING 7839
[例2]列出所有雇员的层次结构
SQL> select lpad(' ',4*(level-1))||ename ename,empno,mgr from scott.emp 2 start with mgr is not null 3 connect by empno=prior mgr;
ENAME EMPNO MGR ------------------ --------- ---------
SMITH 7369 7902 FORD 7902 7566 JONES 7566 7839 KING 7839
ALLEN 7499 7698 BLAKE 7698 7839 KING 7839
WARD 7521 7698 BLAKE 7698 7839 KING 7839
JONES 7566 7839 KING 7839
MARTIN 7654 7698 BLAKE 7698 7839 KING 7839
BLAKE 7698 7839 KING 7839
CLARK 7782 7839
KING 7839
SCOTT 7788 7566 JONES 7566 7839
以往的查询SQL:
Select area,month,sum(money) from SaleOrder group by area,month 然后广州,深圳的合计和所有地区合计都需要在程序里自行累计
1.其实可以使用如下SQL:
Select area,month,sum(total_sale) from SaleOrder group by rollup(area,month) 就能产生和报表一模一样的纪录
2.如果year不想累加,可以写成
Select year,month,area,sum(total_sale) from SaleOrder group by year, rollup(month,area) 另外Oracle 9i还支持如下语法:
Select year,month,area,sum(total_sale) from SaleOrder group by rollup((year,month),area)
3.如果使用Cube(area,month)而不是RollUp(area,month),除了获得每个地区的合计之外,还将获得每个月份的合计,在报表最后显示。
4.Grouping让合计列更好读
RollUp在显示广州合计时,月份列为NULL,但更好的做法应该是显示为\"所有月份\"
Grouping就是用来判断当前Column是否是一个合计列,1为yes,然后用Decode把它转为\"所有月份\"
Select Decode(Grouping(area),1,'所有地区',area) area, Decode(Grouping(month),1,'所有月份',month),
sum(money)
From SaleOrder
Group by RollUp(area,month);
2.对多级层次查询的start with.....connect by
比如人员组织,产品类别,Oracle提供了很经典的方法 SELECT LEVEL, name, emp_id,manager_emp_id FROM employee
START WITH manager_emp_id is null
CONNECT BY PRIOR emp_id = manager_emp_id;
上面的语句demo了全部的应用,start with指明从哪里开始遍历树,如果从根开始,那么它的manager应该是Null,如果从某个职员开始,可以写成emp_id='11'
CONNECT BY 就是指明父子关系,注意PRIOR位置 另外还有一个LEVEL列,显示节点的层次
3.更多报表/分析决策功能 3.1 分析功能的基本结构
分析功能() over( partion子句,order by子句,窗口子句) 概念上很难讲清楚,还是用例子说话比较好.
3.2 Row_Number 和 Rank, DENSE_Rank 用于选出Top 3 sales这样的报表
当两个业务员可能有相同业绩时,就要使用Rank和Dense_Rank 比如
金额 RowNum Rank Dense_Rank 张三 4000元 1 1 1 李四 3000元 2 2 2 钱五 2000元 3 3 3 孙六 2000元 4 3 3 丁七 1000元 5 5 4
这时,应该把并列第三的钱五和孙六都选进去,所以用Ranking功能比RowNumber保险.至于Desnse还是Ranking就看具体情况了。 SELECT salesperson_id, SUM(tot_sales) sp_sales,
RANK( ) OVER (ORDER BY SUM(tot_sales) DESC) sales_rank FROM orders
GROUP BY salesperson_id
3.3 NTILE 把纪录帄分成甲乙丙丁四等
比如我想取得前25%的纪录,或者把25%的纪录当作同一个level帄等对待,把另25%当作另一个Level帄等对待
SELECT cust_nbr, SUM(tot_sales) cust_sales,
NTILE(4) OVER (ORDER BY SUM(tot_sales) DESC) sales_quartile FROM orders GROUP BY cust_nbr ORDER BY 3,2 DESC;
NTITLE(4)把纪录以 SUM(tot_sales)排序分成4份.
3.4 辅助分析列和Windows Function
报表除了基本事实数据外,总希望旁边多些全年总销量,到目前为止的累计销量,前后三个月的帄均销量这样的列来参考.
这种前后三个月的帄均和到目前为止的累计销量就叫windows function,是SQL2003的针对OLAP的新函数, 见下例
SELECT month, SUM(tot_sales) monthly_sales, SUM(SUM(tot_sales)) OVER (ORDER BY month
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_preceeding FROM orders GROUP BY month ORDER BY month;
SELECT month, SUM(tot_sales) monthly_sales, AVG(SUM(tot_sales)) OVER (ORDER BY month
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) rolling_avg FROM orders GROUP BY month ORDER BY month;
Windows Function的关键就是Windows子句的几个取值 1 PRECEDING 之前的一条记录 1 FOLLOWING 之后的一条记录
UNBOUNDED PRECEDING 之前的所有记录 CURRENT ROW 当前纪录
4.SubQuery总结
SubQuery天天用了,理论上总结一下.SubQuery 分三种 1.Noncorrelated 子查询 最普通的样式.
2.Correlated Subqueries 把父查询的列拉到子查询里面去,头一回cyt教我的时候理解了半天. 3.Inline View 也被当成最普通的样式用了.
然后Noncorrelated 子查询又有三种情况
1.返回一行一列 where price < (select max(price) from goods )
2.返回多行一列 where price>= ALL (select price from goods where type=2)
or where NOT price< ANY(select price from goods where type=2)
最常用的IN其实就是=ANY()
3.返回多行多列 一次返回多列当然就节省了查询时间 UPDATE monthly_orders SET (tot_orders, max_order_amt) =
(SELECT COUNT(*), MAX(sale_price) FROM cust_order)
DELETE FROM line_item WHERE (order_nbr, part_nbr) IN
(SELECT order_nbr, part_nbr FROM cust_order c)
层次结构查询(Querying Hierarchies)
层次结构在现实生活中是很常见的,因此早期的数据库是层次数据库,如实的反映了这种关系。后来层次数据库被证实是非常笨重的,因此关系数据库取得了成功。但是层次结构依然存在,而在关系数据库中层次并不能得到很好的体现。ORACLE提供了强大的层次结构查询来解决这个问题。
一、 固定和可变层数
层次结构关系大致可分为两种。
一种是层级固定,例如美国分为各个州,州下分县,县下又分镇区和城市。这种情况如果在一张表中用递归来实现并不是很好的选择,将其保存在主从结构的三张表中更容易查询和操作。 另一种是层级可变。如图所示的组件清单:
可构造层次表如下:
二、 ORACLE的层次查询
ORACLE提供了SELECT查询语句的START WITH和CONNECT BY子句,来实现层次查询。要进行层次查询,必须明了两件事情:识别根记录的条件和子记录中指向其父亲的列名。
看我们的例子,parent_assembly列决定该记录的父亲。由于根记录Airplane没有父亲,因此列parent_assembly为空,而其他记录的该列不为空,凭此可识别根记录。两个条件均已满足,构造查询语句如下:
SELECT assembly_id, assembly_name, parent_assembly FROM bill_of_materials
START WITH parent_assembly IS NULL
CONNECT BY parent_assembly = PRIOR assembly_id; 三、 解析CONNECT BY子句
从一定角度看,CONNECT BY可以看作是一种连接的规范。实现递归连接,也可以使用标准sql语句,将BILL_OF_MATERIAL与自身连接起来并返回每行的父记录。 SELECT bom1.assembly_id, bom1.assembly_name, bom2.assembly_id parent FROM bill_of_materials bom1 LEFT OUTER JOIN bill_of_materials bom2 ON bom1.parent_assembly = bom2.assembly_id;
这个sql语句返回的记录和上面那个语句是同样的。唯一的区别是行的顺序不一样。当然行的顺序是非常重要的,自连接的sql语句返回记录的顺序不能反映父子关系。
很多问题并不能通过自连接解决,例如你不能使用自连接查询选出刚好组成一架飞机的组件。但是CONNECT BY可以解决所有父子结构方面的问题。 四、 如何使用WHERE子句
要过滤记录,有两种途径。首先,从查询的根节点方面着手,使用START WITH子句来限制查询特定节点。例如我们可以使用如下语句,只查询飞机上两个座位及其部件的信息: SELECT assembly_id, assembly_name, PRIOR assembly_id parent FROM bill_of_materials
START WITH assembly_id IN (205,206)
CONNECT BY parent_assembly = PRIOR assembly_id;
但是有时需要其他条件,比如part_number is not null,这时就需要引入WHERE子句。 SELECT assembly_id, assembly_name, PRIOR assembly_id parent FROM bill_of_materials
WHERE PART_NUMBER IS NOT NULL START WITH assembly_id IN (205,206)
CONNECT BY parent_assembly = PRIOR assembly_id;
上面语句中的过滤条件是在CONNECT BY语句处理完成后才发生作用。当树已经生成,过滤条件才开始影响查询所返回的记录。 五、 层次连接
要写好层次查询,必须了解ORACLE如何处理这样的查询。 首先实现连接
其次CONNECT BY处理应用在连接所返回的记录行上。
再次WHERE子句定义的过滤条件应用在CONNECT BY操作返回的记录行上。 知道了上述顺序,让我们来看一个查询:
SELECT assembly_id id, parent_assembly parent, assembly_name name, bom.part_number part, current_inventory inventory FROM bill_of_materials bom, part p WHERE bom.part_number = p.part_number START WITH assembly_id = 200
CONNECT BY parent_assembly = PRIOR assembly_id;
上面的查询中,首先处理的是连接——bom.part_number = p.part_number。
而在bill_of_materials表中只有最低层次的节点(叶节点)其part_number列才不为空。两者连接后,将bill_of_materials表中part_number为空的列剔除了,而那些列又是叶节点的父级。所以下一步进行CONNECT BY操作时,由于找不到查询的根节点——assembly_id = 200,查询不会返回任何记录。注意:没有根就意味着没有输出。
解决办法是使用外连接(+),这样CONNECT BY操作才会成功。
SELECT assembly_id id, parent_assembly parent, assembly_name name, bom.part_number part, current_inventory inventory FROM bill_of_materials bom, part p
WHERE bom.part_number = p.part_number(+) START WITH assembly_id = 200
CONNECT BY parent_assembly = PRIOR assembly_id;
WHERE子句里的连接条件是首先被处理的,而WHERE子句里的过滤条件是在CONNECT BY处理完成后才起作用的。
如下述查询选出备件所有存货量小于500的记录:
SELECT assembly_id id, parent_assembly parent, assembly_name name, bom.part_number part, current_inventory inventory FROM bill_of_materials bom, part p
WHERE bom.part_number = p.part_number (+) AND p.current_inventory < 500 START WITH assembly_id = 200
CONNECT BY parent_assembly = PRIOR assembly_id; AND p.current_inventory < 500属于过滤条件,最后处理。
你也许注意到上面的查询都是用的老版本的连接,很不幸的,Oracle 9i数据库在CONNNECT BY查询和新的JOIN子句之间存在bug。上述语句用新JOIN子句写出: SELECT assembly_id id, parent_assembly parent, assembly_name name, bom.part_number part, current_inventory inventory FROM bill_of_materials bom LEFT OUTER JOIN part p ON bom.part_number = p.part_number WHERE p.current_inventory < 500
START WITH assembly_id = 200
CONNECT BY parent_assembly = PRIOR assembly_id;
这个语句将不会返回任何值,因为根节点被过早的过滤掉了。 当然ORACLE已经清楚这个bug,正在修复。 六、 层级
我们经常希望得知树中给定记录的层级,oracle提供LEVEL伪列来实现。LEVEL可出现在SQL语句的任何部分,并且对于根节点返回1,根节点的孩子返回2,孙子返回3…。 通常使用LEVEL来实现记录的缩排,以便更清楚的表现树状结构。 七、 节点路径
Oracle 9i提供了新函数SYS_CONNECT_BY_PATH,可以给出从根节点到指定节点的全路径。该函数有两个参数:列名和分隔符,而且列名并不局限于CONNECT BY列。 例如:
SELECT SYS_CONNECT_BY_PATH(assembly_name,'/') name_path FROM bill_of_materials WHERE part_number = 1019
START WITH parent_assembly IS NULL
CONNECT BY parent_assembly = PRIOR assembly_id; 八、 排序
想对结果进行排序,当然可以使用标准的ORDER BY子句,但是它会破坏父子结构,一般不建议使用。
Oracle 9i也提供了一个ORDER BY子句的关键字SIBLINGS,允许对层次数据以有意义的方式进行分类,同时保留层次结构。 如:
SELECT RPAD(' ', 2*(LEVEL-1)) || assembly_name assembly_name FROM bill_of_materials START WITH assembly_id = 200
CONNECT BY parent_assembly = PRIOR assembly_id ORDER SIBLINGS BY assembly_name;
表示在每个层级(LEVEL)上按字母表顺序进行排序,这对生成树状报表非常有用。 用connect by及start with建立树结构的报表并不难,但必须遵守下列规则: .使用connect by时子句的顺序是 select from where start with connect by order by
.prior使报表从树根到树叶(如果prior列是父母),或从树叶至树根(如果prior列是孩子)。
.where子句将从树中去掉单个节点,但保留她它们的后代(或祖先,假设PRIOR在等号的右边)。 .connect by中的限定(尤其是不等时)将去掉单个节点及其后代(或祖先,取决于浏览树的方向)。 .connect by不能用在where子句的连接表(table join)中。
Oracle数据库中分区表的操作方法 Oracle数据库中分区表的操作方法
在大型的企业应用或企业级的数据库应用中,要处理的数据量通常可以达到几十到几百GB,有的甚至可以到TB级。虽然存储介质和数据处理技术的发展也很快,但是仍然不能满足用户的需求,为了使用户的大量的数据在读写操作和查询中速度更快,Oracle提供了对表和索引进行分区的技术,以改善大型应用系统的性能。
使用分区的优点:
·增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用;
·维护方便:如果表的某个分区出现故障,需要修复数据,只修复该分区即可;
·均衡I/O:可以把不同的分区映射到磁盘以帄衡I/O,改善整个系统性能;
·改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度。
Oracle数据库提供对表或索引的分区方法有三种:
·范围分区
·Hash分区(散列分区)
·复合分区
下面将以实例的方式分别对这三种分区方法来说明分区表的使用。为了测试方便,我们先建三个表空间。
create tablespace dinya_space01 datafile ’/test/demo/oracle/demodata/dinya01.dnf’ size 50M create tablespace dinya_space01 datafile ’/test/demo/oracle/demodata/dinya02.dnf’ size 50M create tablespace dinya_space01 datafile ’/test/demo/oracle/demodata/dinya03.dnf’ size 50M
1.1. 分区表的创建
1.1.1. 范围分区
范围分区就是对数据表中的某个值的范围进行分区,根据某个值的范围,决定将该数据存储在哪个分区上。如根据序号分区,根据业务记录的创建日期进行分区等。
需求描述:有一个物料交易表,表名:material_transactions。该表将来可能有千万级的数据记录数。要求在建该表的时候使用分区表。这时候我们可以使用序号分区三个区,每个区中预计存储三千万的数据,也可以使用日期分区,如每五年的数据存储在一个分区上。
根据交易记录的序号分区建表:
SQL> create table dinya_test 2 ( 3 transaction_id number primary key, 4 item_id number(8) not null, 5 item_description varchar2(300), 6 transaction_date date not null 7 ) 8 partition by range (transaction_id) 9 ( 10 partition part_01 values less than(30000000) tablespace dinya_space01, 11 partition part_02 values less than(60000000) tablespace dinya_space02, 12 partition part_03 values less than(maxvalue) tablespace dinya_space03 13 ); Table created.
建表成功,根据交易的序号,交易ID在三千万以下的记录将存储在第一个表空间dinya_space01中,分区名为:par_01,在三千万到六千万之间的记录存储在第二个表空间:
dinya_space02中,分区名为:par_02,而交易ID在六千万以上的记录存储在第三个表空间
dinya_space03中,分区名为par_03.
根据交易日期分区建表:
SQL> create table dinya_test 2 ( 3 transaction_id number primary key, 4 item_id number(8) not null, 5 item_description varchar2(300), 6 transaction_date date not null 7 ) 8 partition by range (transaction_date) 9 ( 10 partition part_01 values less than(to_date(’2006-01-01’,’yyyy-mm-dd’)) tablespace dinya_space01, 11 partition part_02 values less than(to_date(’2010-01-01’,’yyyy-mm-dd’)) tablespace dinya_space02, 12 partition part_03 values less than(maxvalue) tablespace dinya_space03 13 ); Table created.
这样我们就分别建了以交易序号和交易日期来分区的分区表。每次插入数据的时候,系统将根据指定的字段的值来自动将记录存储到制定的分区(表空间)中。
当然,我们还可以根据需求,使用两个字段的范围分布来分区,如partition by range ( transaction_id ,transaction_date), 分区条件中的值也做相应的改变,请读者自行测试。
1.1.2. Hash分区(散列分区)
散列分区为通过指定分区编号来均匀分布数据的一种分区类型,因为通过在I/O设备上进行散列分区,使得这些分区大小一致。如将物料交易表的数据根据交易ID散列地存放在指定的三个表空间中:
SQL> create table dinya_test 2 ( 3 transaction_id number primary key, 4 item_id number(8) not null, 5 item_description varchar2(300), 6 transaction_date date 7 ) 8 partition by hash(transaction_id) 9 ( 10 partition part_01 tablespace dinya_space01, 11 partition part_02 tablespace dinya_space02, 12 partition part_03 tablespace dinya_space03 13 ); Table created.
建表成功,此时插入数据,系统将按transaction_id将记录散列地插入三个分区中,这里也就是三个不同的表空间中。
1.1.3. 复合分区
有时候我们需要根据范围分区后,每个分区内的数据再散列地分布在几个表空间中,这样我们就要使用复合分区。复合分区是先使用范围分区,然后在每个分区内再使用散列分区的一种分区方法,如将物料交易的记录按时间分区,然后每个分区中的数据分三个子分区,将数据散列地存储在三个指定的表空间中:
SQL> create table dinya_test 2 ( 3 transaction_id number primary key, 4 item_id number(8) not null, 5 item_description varchar2(300), 6 transaction_date date 7 ) 8 partition by range(transaction_date)subpartition by hash(transaction_id) 9 subpartitions 3 store in (dinya_space01,dinya_space02,dinya_space03) 10 ( 11 partition part_01 values less than(to_date(’2006-01-01’,’yyyy-mm-dd’)), 12 partition part_02 values less than(to_date(’2010-01-01’,’yyyy-mm-dd’)), 13 partition part_03 values less than(maxvalue) 14 ); Table created.
该例中,先是根据交易日期进行范围分区,然后根据交易的ID将记录散列地存储在三个表空间中。 1.2. 分区表操作
以上了解了三种分区表的建表方法,下面将使用实际的数据并针对按日期的范围分区来测试分区表的数据记录的操作。
1.2.1. 插入记录:
SQL> insert into dinya_test values(1,12,’BOOKS’,sysdate); 1 row created. SQL> insert into dinya_test values(2,12, ’BOOKS’,sysdate+30); 1 row created. SQL> insert into dinya_test values(3,12, ’BOOKS’,to_date(’2006-05-30’,’yyyy-mm-dd’)); 1 row created. SQL> insert into dinya_test values(4,12, ’BOOKS’,to_date(’2007-06-23’,’yyyy-mm-dd’)); 1 row created. SQL> insert into dinya_test values(5,12, ’BOOKS’,to_date(’2011-02-26’,’yyyy-mm-dd’)); 1 row created. SQL> insert into dinya_test values(6,12, ’BOOKS’,to_date(’2011-04-30’,’yyyy-mm-dd’)); 1 row created. SQL> commit; Commit complete. SQL>
按上面的建表结果,2006年前的数据将存储在第一个分区part_01上,而2006年到2010年的交易数据将存储在第二个分区part_02上,2010年以后的记录存储在第三个分区part_03上。
1.2.2. 查询分区表记录:
SQL> select * from dinya_test partition(part_01); TRANSACTION_ID ITEM_ID ITEM_DESCRIPTION TRANSACTION_DATE -------------------------------------------------------------------------------- 1 12 BOOKS 2005-1-14 14:19: 2 12 BOOKS 2005-2-13 14:19: SQL> SQL> select * from dinya_test partition(part_02); TRANSACTION_ID ITEM_ID ITEM_DESCRIPTION TRANSACTION_DATE -------------------------------------------------------------------------------- 3 12 BOOKS 2006-5-30 4 12 BOOKS 2007-6-23 SQL> SQL> select * from dinya_test partition(part_03); TRANSACTION_ID ITEM_ID ITEM_DESCRIPTION TRANSACTION_DATE -------------------------------------------------------------------------------- 5 12 BOOKS 2011-2-26 6 12 BOOKS 2011-4-30 SQL>
从查询的结果可以看出,插入的数据已经根据交易时间范围存储在不同的分区中。这里是指定了分区的查询,当然也可以不指定分区,直接执行select * from dinya_test查询全部记录。
在也检索的数据量很大的时候,指定分区会大大提高检索速度。
1.2.3. 更新分区表的记录:
SQL> update dinya_test partition(part_01) t set t.item_description=’DESK’ where t.transaction_id=1; 1 row updated. SQL> commit; Commit complete. SQL>
这里将第一个分区中的交易ID=1的记录中的item_description字段更新为“DESK”,可以看到已经成功更新了一条记录。但是当更新的时候指定了分区,而根据查询的记录不在该分区中时,将不会更新数据,请看下面的例子:
SQL> update dinya_test partition(part_01) t set t.item_description=’DESK’ where t.transaction_id=6; 0 rows updated. SQL> commit; Commit complete. SQL>
指定了在第一个分区中更新记录,但是条件中限制交易ID为6,而查询全表,交易ID为6的记录在第三个分区中,这样该条语句将不会更新记录。
1.2.4. 删除分区表记录:
SQL> delete from dinya_test partition(part_02) t where t.transaction_id=4; 1 row deleted. SQL> commit; Commit complete. SQL>
上面例子删除了第二个分区part_02中的交易记录ID为4的一条记录,和更新数据相同,如果指定了分区,而条件中的数据又不在该分区中时,将不会删除任何数据。
1.3. 分区表索引的使用:
分区表和一般表一样可以建立索引,分区表可以创建局部索引和全局索引。当分区中出现许多事务并且要保证所有分区中的数据记录的唯一性时采用全局索引。
1.3.1. 局部索引分区的建立:
SQL> create index dinya_idx_t on dinya_test(item_id) 2 local 3 ( 4 partition idx_1 tablespace dinya_space01, 5 partition idx_2 tablespace dinya_space02, 6 partition idx_3 tablespace dinya_space03 7 ); Index created. SQL>
看查询的执行计划,从下面的执行计划可以看出,系统已经使用了索引:
SQL> select * from dinya_test partition(part_01) t where t.item_id=12; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=187) 1 0 TABLE ACCESS (BY LOCAL INDEX ROWID) OF ’DINYA_TEST’ (Cost= 2 Card=1 Bytes=187) 2 1 INDEX (RANGE SCAN) OF ’DINYA_IDX_T’ (NON-UNIQUE) (Cost=1 Card=1) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 4 consistent gets 0 physical reads 0 redo size 334 bytes sent via SQL*Net to client 309 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 2 rows processed SQL>
1.3.2. 全局索引分区的建立
全局索引建立时global 子句允许指定索引的范围值,这个范围值为索引字段的范围值:
SQL> create index dinya_idx_t on dinya_test(item_id) 2 global partition by range(item_id) 3 ( 4 partition idx_1 values less than (1000) tablespace dinya_space01, 5 partition idx_2 values less than (10000) tablespace dinya_space02, 6 partition idx_3 values less than (maxvalue) tablespace dinya_space03 7 ); Index created. SQL>
本例中对表的item_id字段建立索引分区,当然也可以不指定索引分区名直接对整个表建立索引,如:
SQL> create index dinya_idx_t on dinya_test(item_id); Index created. SQL>
同样的,对全局索引根据执行计划可以看出索引已经可以使用:
SQL> select * from dinya_test t where t.item_id=12; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=3 Bytes=561) 1 0 TABLE ACCESS (BY GLOBAL INDEX ROWID) OF ’DINYA_TEST’ (Cost =2 Card=3 Bytes=561) 2 1 INDEX (RANGE SCAN) OF ’DINYA_IDX_T’ (NON-UNIQUE) (Cost=1 Card=3) Statistics ---------------------------------------------------------- 5 recursive calls 0 db block gets 10 consistent gets 0 physical reads 0 redo size 420 bytes sent via SQL*Net to client 309 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 3 sorts (memory) 0 sorts (disk) 5 rows processed SQL>
1.4. 分区表的维护:
了解了分区表的建立、索引的建立、表和索引的使用后,在应用的还要经常对分区进行维护和管理。日常维护和管理的内容包括:增加一个分区,合并一个分区及删除分区等等。下面以范围分区为例说明增加、合并、删除分区的一般操作:
1.4.1. 增加一个分区:
SQL> alter table dinya_test 2 add partition part_04 values less than(to_date(’2012-01-01’,’yyyy-mm-dd’)) tablespace dinya_spa ce03; Table altered. SQL>
增加一个分区的时候,增加的分区的条件必须大于现有分区的最大值,否则系统将提示ORA-14074 partition bound must collate higher than that of the last partition 错误。
1.4.2. 合并一个分区:
SQL> alter table dinya_test merge partitions part_01,part_02 into partition part_02; Table altered. SQL>
在本例中将原有的表的part_01分区和part_02分区进行了合并,合并后的分区为part_02,如果在合并的时候把合并后的分区定为part_01的时候,系统将提示ORA-14275 cannot reuse lower-bound partition as resulting partition 错误。
1.4.3. 删除分区:
SQL> alter table dinya_test drop partition part_01; Table altered. SQL>
删除分区表的一个分区后,查询该表的数据时显示,该分区中的数据已全部丢失,所以执行删除分区动作时要慎重,确保先备份数据后再执行,或将分区合并。
一﹑手工创建数据库的全部脚本及说明
手工创建数据库的全部脚本及说明 系统环境:
1、操作系统:Windows 2000 Server,机器内存128M 2、数据库: Oracle 8i R2 (8.1.6) for NT 企业版 3、安装路径:D:\\ORACLE 建库步骤:
1、手工创建相关目录 D:\\Oracle\\admin\est D:\\Oracle\\admin\est\\adhoc D:\\Oracle\\admin\est\\bdump D:\\Oracle\\admin\est\\cdump D:\\Oracle\\admin\est\\create D:\\Oracle\\admin\est\\exp D:\\Oracle\\admin\est\\pfile D:\\Oracle\\admin\est\ D:\\Oracle\\oradata\est D:\\Oracle\\oradata\est\\archive
2、手工创建初始化启动参数文件:D:\\Oracle\\admin\est\\pfile\\inittest.ora,内容: 3、手工创建D:\\Oracle\\Ora81\\DATABASE\\inittest.ora文件,
内容:IFILE='D:\\Oracle\\admin\est\\pfile\\inittest.ora'
4、使用orapwd.exe命令,创建D:\\Oracle\\Ora81\\DATABASE\\PWDtest.ora
命令:D:\\Oracle\\Ora81\\bin\\orapwd file=D:\\Oracle\\Ora81\\DATABASE\\PWDtest.ora password=ORACLE entries=5
5、通过oradim.exe命令,在服务里生成一个新的实例管理服务,启动方式为手工 set ORACLE_SID=test
D:\\Oracle\\Ora81\\bin\\oradim -new -sid test -startmode manual -pfile \"D:\\Oracle\\admin\est\\pfile\\inittest.ora\" 6、生成各种数据库对象 D:\\>svrmgrl --创建数据库
connect INTERNAL/oracle
startup nomount pfile=\"D:\\Oracle\\admin\est\\pfile\\inittest.ora\" CREATE DATABASE test
LOGFILE 'D:\\Oracle\\oradata\est\\redo01.log' SIZE 2048K, 'D:\\Oracle\\oradata\est\\redo02.log' SIZE 2048K, 'D:\\Oracle\\oradata\est\\redo03.log' SIZE 2048K MAXLOGFILES 32 MAXLOGMEMBERS 2 MAXLOGHISTORY 1
DATAFILE 'D:\\Oracle\\oradata\est\\system01.dbf' SIZE 58M REUSE AUTOEXTEND ON NEXT 640K MAXDATAFILES 254 MAXINSTANCES 1
CHARACTER SET ZHS16GBK
NATIONAL CHARACTER SET ZHS16GBK; 控制文件、日志文件在上面语句执行时生成
connect INTERNAL/oracle --修改系统表空间
ALTER TABLESPACE SYSTEM DEFAULT STORAGE ( INITIAL 64K NEXT 64K MINEXTENTS 1 MAXEXTENTS UNLIMITED PCTINCREASE 50); ALTER TABLESPACE SYSTEM MINIMUM EXTENT 64K; --创建回滚表空间
CREATE TABLESPACE RBS DATAFILE 'D:\\Oracle\\oradata\est\\rbs01.dbf' SIZE 256M REUSE AUTOEXTEND ON NEXT 5120K MINIMUM EXTENT 512K
DEFAULT STORAGE ( INITIAL 512K NEXT 512K MINEXTENTS 8 MAXEXTENTS 4096); CREATE PUBLIC ROLLBACK SEGMENT RBS13 TABLESPACE RBS STORAGE ( OPTIMAL 4096K );
CREATE PUBLIC ROLLBACK SEGMENT RBS14 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS15 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS16 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS17 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS18 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS19 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS20 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS21 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS22 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); CREATE PUBLIC ROLLBACK SEGMENT RBS23 TABLESPACE RBS STORAGE ( OPTIMAL 4096K );
CREATE PUBLIC ROLLBACK SEGMENT RBS24 TABLESPACE RBS STORAGE ( OPTIMAL 4096K ); --使回滚段在线
ALTER ROLLBACK SEGMENT \"RBS0\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS1\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS2\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS3\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS4\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS5\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS6\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS7\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS8\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS9\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS10\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS11\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS12\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS13\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS14\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS15\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS16\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS17\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS18\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS19\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS20\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS21\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS22\" ONLINE; ALTER ROLLBACK SEGMENT \"RBS23\" ONLINE;
ALTER ROLLBACK SEGMENT \"RBS24\" ONLINE; --修改sys用户的临时表空间为TEMP alter user sys temporary tablespace TEMP; --创建数据字典表
@D:\\Oracle\\Ora81\\Rdbms\\admin\\catalog.sql; @D:\\Oracle\\Ora81\\Rdbms\\admin\\catexp7.sql @D:\\Oracle\\Ora81\\Rdbms\\admin\\catproc.sql @D:\\Oracle\\Ora81\\Rdbms\\admin\\caths.sql connect system/manager
@D:\\Oracle\\Ora81\\sqlplus\\admin\\pupbld.sql connect internal/oracle
@D:\\Oracle\\Ora81\\Rdbms\\admin\\catrep.sql exit
--生成SQL*Plus帮助系统 sqlplus SYSTEM/manager
@D:\\Oracle\\Ora81\\sqlplus\\admin\\help\\helpbld.sql helpus.sql exit
--修改system用户默认表空间和临时表空间 svrmgrl
connect internal/oracle
alter user system default tablespace TOOLS; alter user system temporary tablespace TEMP; exit
7、将test实例启动服务设置成自动启动方式 D:\\Oracle\\Ora81\\bin\\oradim -edit -sid test -startmode auto
----------------init.ora 内容-------------------------- db_name = \"test\" instance_name = test service_names = test db_files = 1024
control_files = (\"D:\\Oracle\\oradata\est\\control01.ctl\\"D:\\Oracle\\oradata\est\\control03.ctl\") open_cursors = 200 max_enabled_roles = 30
db_file_multiblock_read_count = 8 db_block_buffers = 4096 shared_pool_size = 52428800
large_pool_size = 78643200 java_pool_size = 20971520 log_checkpoint_interval = 10000 log_checkpoint_timeout = 1800 processes = 115 parallel_max_servers = 5 log_buffer = 32768 max_dump_file_size = 10240 global_names = true
oracle_trace_collection_name = \"\"
background_dump_dest = D:\\Oracle\\admin\est\\bdump user_dump_dest = D:\\Oracle\\admin\est\ db_block_size = 16384
remote_login_passwordfile = exclusive os_authent_prefix = \"\" job_queue_processes = 4 job_queue_interval = 60 open_links = 4
distributed_transactions = 10
mts_dispatchers = \"(PROTOCOL=TCP)(PRE=oracle.aurora.server.SGiopServer)\" mts_dispatchers = \"(protocol=TCP)\"
二﹑Oracle数据字典
以下表格中收集了Oracle数据字典中几乎所有的视图或别名,至于每个视图或别名的字段请用‘Describe’语句来查询。 命名说明:
Oracle数据字典中,对象名称多数以\"USER.\",\"ALL.\",\"DBA.\"为前缀\"USER.\"视图中记录通常记录执行查询的帐户所拥有的对象的信息,\"ALL.\"视图中记录包括\"USER\"记录和授权至PUBLIC或用户的对象的信息,\"DBA.\"视图包含所有数据库对象,而不管其所有者。 使用举例:
select * from dba_data_files
查询表空间的信息(当前用户必须拥有DBA角色)
select owner , object_name ,object_type from all_objects 查询某一用户下的所有表、过程、函数等信息
存储过程
1 CREATE OR REPLACE PROCEDURE 存储过程名 2 IS 3 BEGIN 4 NULL; 5 END; 行1:
CREATE OR REPLACE PROCEDURE 是一个SQL语句通知Oracle数据库去创建一个叫做skeleton存储过程, 如果存在就覆盖它; 行2:
IS关键词表明后面将跟随一个PL/SQL体。 行3:
BEGIN关键词表明PL/SQL体的开始。 行4:
NULL PL/SQL语句表明什么事都不做,这句不能删去,因为PL/SQL体中至少需要有一句;
行5:
END关键词表明PL/SQL体的结束
存储过程创建语法:
create or replace procedure 存储过程名(param1 in type,param2 out type) as
变量1 类型(值范围); --vs_msg VARCHAR2(4000); 变量2 类型(值范围); Begin
Select count(*) into 变量1 from 表A where列名=param1;
If (判断条件) then
Select 列名 into 变量2 from 表A where列名=param1;
Dbms_output。Put_line(‘打印信息’); Elsif (判断条件) then
Dbms_output。Put_line(‘打印信息’);
Else
Raise 异常名(NO_DATA_FOUND); End if; Exception
When others then Rollback; End;
注意事项:
1, 存储过程参数不带取值范围,in表示传入,out表示
输出
类型可以使用任意Oracle中的合法类型。 2, 变量带取值范围,后面接分号
3, 在判断语句前最好先用count(*)函数判断是否存在该条操作记录
4, 用select 。。。into。。。给变量赋值 5, 在代码中抛异常用 raise+异常名
CREATE OR REPLACE PROCEDURE存储过程名 (
--定义参数
is_ym IN CHAR(6) , the_count OUT NUMBER, ) AS --定义变量
vs_msg VARCHAR2(4000); --错误信息变量 vs_ym_beg CHAR(6); --起始月份 vs_ym_end CHAR(6); --终止月份 vs_ym_sn_beg CHAR(6); --同期起始月份 vs_ym_sn_end CHAR(6); --同期终止月份
--定义游标(简单的说就是一个可以遍历的结果集)
CURSOR cur_1 IS SELECT 。。。 FROM 。。。 WHERE 。。。 GROUP BY 。。。; BEGIN
--用输入参数给变量赋初值,用到了Oralce的SUBSTR TO_CHAR ADD_MONTHS
TO_DATE 等很常用的函数。 vs_ym_beg := SUBSTR(is_ym,1,6); vs_ym_end := SUBSTR(is_ym,7,6); vs_ym_sn_beg :=
TO_CHAR(ADD_MONTHS(TO_DATE(vs_ym_beg,'yyyymm'), -12),'yyyymm'); vs_ym_sn_end :=
TO_CHAR(ADD_MONTHS(TO_DATE(vs_ym_end,'yyyymm'), -12),'yyyymm');
--先删除表中特定条件的数据。
DELETE FROM 表名 WHERE ym = is_ym;
--然后用内置的DBMS_OUTPUT对象的put_line方法打印出影响的记录行数,其中用到一个系统变量SQL%rowcount
DBMS_OUTPUT.put_line('del上月记录='||SQL%rowcount||'条');
INSERT INTO表名
(area_code,ym,CMCODE,rmb_amt,usd_amt) SELECT
area_code,is_ym,CMCODE,SUM(rmb_amt)/10000,SUM(usd_amt)/10000
FROM BGD_AREA_CM_M_BASE_T WHERE ym >= vs_ym_beg AND ym <= vs_ym_end GROUP BY area_code,CMCODE;
DBMS_OUTPUT.put_line('ins当月记录='||SQL%rowcount||'条');
--遍历游标处理后更新到表。遍历游标有几种方法,用for语句是其中比较直观的一种。
FOR rec IN cur_1 LOOP UPDATE 表名
SET rmb_amt_sn = rec.rmb_amt_sn,usd_amt_sn = rec.usd_amt_sn
WHERE area_code = rec.area_code AND CMCODE = rec.CMCODE AND ym = is_ym; END LOOP;
COMMIT;
--错误处理部分。OTHERS表示除了声明外的任意错误。SQLERRM是系统内置变量保存了当前错误的详细信息。
EXCEPTION
WHEN OTHERS THEN vs_msg := 'ERROR IN
xxxxxxxxxxx_p('||is_ym||'):'||SUBSTR(SQLERRM,1,500);
ROLLBACK;
--把当前错误记录进日志表。
INSERT INTO
LOG_INFO(proc_name,error_info,op_date) VALUES('xxxxxxxxxxx_p',vs_msg,SYSDATE); COMMIT; RETURN; END;
oracle存储过程语法
1 、判断语句:
if 比较式 then begin end; end if;
create or replace procedure test(x in number) is begin
if x >0 then begin x := 0 - x; end; end if; if x = 0 then begin x: = 1; end; end if; end test; 2 、For 循环 For ... in ... LOOP
-- 执行语句 end LOOP; (1) 循环遍历游标
create or replace procedure test() as
Cursor cursor is select name from student; name varchar(20);
begin
for name in cursor LOOP begin
dbms_output.putline(name); end; end LOOP; end test; (2) 循环遍历数组
create or replace procedure test(varArray in myPackage.TestArray) as
--( 输入参数varArray 是自定义的数组类型,定义方式见标题6)
i number; begin
i := 1; -- 存储过程数组是起始位置是从1 开始的,与java 、C 、C++ 等语言不同。因为在Oracle 中本是没有数组的概念的,数组其实就是一张
-- 表(Table), 每个数组元素就是表中的一个记录,所以遍历数组时就相当于从表中的第一条记录开始遍历
for i in 1..varArray.count LOOP
dbms_output.putline('The No.'|| i || 'record in varArray is:'||varArray(i));
end LOOP; end test;
3 、While 循环 while 条件语句 LOOP begin end; end LOOP; E.g
create or replace procedure test(i in number) as begin
while i < 10 LOOP begin i:= i + 1; end; end LOOP; end test;
4 、数组
首先明确一个概念:Oracle 中本是没有数组的概念的,数组其实就是一张表(Table), 每个数组元素就是表中的一个记录。
使用数组时,用户可以使用Oracle 已经定义好的数组类型,或可根据自己的需要定义数组类型。
(1) 使用Oracle 自带的数组类型 x array; -- 使用时需要需要进行初始化 e.g:
create or replace procedure test(y out array) is x array; begin
x := new array(); y := x; end test;
(2) 自定义的数组类型 ( 自定义数据类型时,建议通过创建Package 的方式实现,以便于管理)
create or replace package myPackage is Public type declarations type info is record( name varchar(20), y number);
type TestArray is table of info index by binary_integer;
-- 此处声明了一个TestArray 的类型数据,其实其为一张存储Info 数据类型的Table 而已,及TestArray 就是一张表,有两个字段,一个是name ,一个是y 。需要注意的是此处使用了Index by binary_integer 编制该Table 的索引项,也可以不写,直接写成:type TestArray is
table of info ,如果不写的话使用数组时就需要进行初始化:varArray myPackage.TestArray; varArray := new myPackage.TestArray();
end TestArray;
5. 游标的使用 Oracle 中Cursor 是非常有用的,用于遍历临时表中的查询结果。其相关方法和属性也很多,现仅就常用的用法做一二介绍:
(1)Cursor 型游标( 不能用于参数传递) create or replace procedure test() is
cusor_1 Cursor is select std_name from student where ...; --Cursor 的使用方式1 cursor_2 Cursor;
begin
select class_name into cursor_2 from class where ...; --Cursor 的使用方式2
可使用For x in cursor LOOP .... end LOOP; 来实现对Cursor 的遍历
end test;
(2)SYS_REFCURSOR 型游标,该游标是Oracle 以预先定义的游标,可作出参数进行传递
create or replace procedure test(rsCursor out SYS_REFCURSOR) is
cursor SYS_REFCURSOR; name varhcar(20); begin
OPEN cursor FOR select name from student where ... --SYS_REFCURSOR 只能通过OPEN 方法来打开和赋值
LOOP
fetch cursor into name --SYS_REFCURSOR 只能通过fetch into 来打开和遍历 exit when
cursor%NOTFOUND; --SYS_REFCURSOR 中可使用三个状态属
性: ---%NOTFOUND( 未找到记录信息) %FOUND( 找到记录信
息) ---%ROWCOUNT( 然后当前游标所指向的行位置)
dbms_output.putline(name); end LOOP; rsCursor := cursor; end test;
实例
下面写一个简单的例子来对以上所说的存储过程的用法做一个应用:
现假设存在两张表,一张是学生成绩表(studnet) ,字段为:
stdId,math,article,language,music,sport,total,average,step
一张是学生课外成绩表(out_school), 字段为:stdId,parctice,comment
通过存储过程自动计算出每位学生的总成绩和平均成绩,同时,如果学生在课外课程中获得的评价为A ,就在总成绩上加20 分。
create or replace procedure autocomputer(step in number) is
rsCursor SYS_REFCURSOR; commentArray myPackage.myArray; math number; article number; language number; music number; sport number; total number;
average number; stdId varchar(30);
record myPackage.stdInfo; i number; begin i := 1;
get_comment(commentArray); -- 调用名为get_comment() 的存储过程获取学生课外评分信息
OPEN rsCursor for select
stdId,math,article,language,music,sport from student t where t.step = step;
LOOP
fetch rsCursor into
stdId,math,article,language,music,sport; exit when rsCursor%NOTFOUND;
total := math + article + language + music + sport; for i in 1..commentArray.count LOOP record := commentArray(i); if stdId = record.stdId then begin
if record.comment = 'A' then begin total := total + 20;
go to next; -- 使用go to 跳出for 循环 end; end if; end; end if; end LOOP;
< update student t set t.total=total and t.average = average where t.stdId = stdId; end LOOP; end; end autocomputer; -- 取得学生评论信息的存储过程 create or replace procedure get_comment(commentArray out myPackage.myArray) is rs SYS_REFCURSOR ; record myPackage.stdInfo; stdId varchar(30); comment varchar(1); i number; begin open rs for select stdId,comment from out_school i := 1; LOOP fetch rs into stdId,comment; exit when rs%NOTFOUND; record.stdId := stdId; record.comment := comment; recommentArray(i) := record; i:=i + 1; end LOOP; end get_comment; -- 定义数组类型myArray create or replace package myPackage is begin type stdInfo is record(stdId varchar(30),comment varchar(1)); type myArray is table of stdInfo index by binary_integer; end myPackage; byebye 因篇幅问题不能全部显示,请点此查看更多更全内容