您的位置:首页 > 数据库 > Oracle

IT忍者神龟之oracle高级CRUD

2014-09-19 13:07 441 查看


SQL Cookbook—插入、更新与删除

涉及到的问题

–1、从一个表向另外的表中复制行

–2、复制表定义(包含表记录)

–3、一次向多个表中插入记录

–4、

–5、当相应行存在时更新

–6、用其他表中的值更新

–7、删除违反参照完整性的记录

–1、从一个表向另外的表中复制行

insert into dept_test (deptno, dname, loc) select deptno, dname, loc from dept where deptno=10

–2、复制表定义(包含表记录)

create table dept_test as select deptno, dname, loc from dept where deptno=10

–3、一次向多个表中插入记录

问题描述

有时需要将一个表中的数据按照一定的条件分别一次性插入到多个表中,怎样实现?

insert all

when loc in (upper('new york', upper('boston'))) then

into dept_test_1 (deptno, dname, loc) values (deptno, dname, loc)

when loc=upper('chicago') then

into dept_test_2 (deptno, dname, loc) values (deptno, dname, loc)

else

into dept_test_3 (deptno, dname, loc) values (deptno, dname, loc)

select deptno, dname, loc from dept

–4、

insert into (select empno, ename, job from emp) values (1, 'zhangsan', 'java')

–5、当相应行存在时更新

方法一

UPDATE emp

SET sal=sal*1.20

WHERE empno in (SELECT empno FROM emp_bonus)

方法二

UPDATE emp

SET sal=sal*1.20

WHERE exists (SELECT null FROM emp_bonus where emp.empno=emp_bonus.empno)

–6、用其他表中的值更新

UPDATE emp e

SET (e.sla, e.comm)=(SELECT ns.sal, ns.sal/2

FROM new_sal ns

WHERE ns.deptno=e.deptno)

WHERE EXISTS( SELECT NULL

FROM new_sal ns

WHERE ns.deptno=e.deptno)

–7、删除违反参照完整性的记录

例如,某些员工被分配到了一个不存在的部门中,要将这些员工删除。

方法一(推荐)

delete from emp where not exists (select * from dept where dept.deptno=emp.deptno)

方法二

delete from emp where emp.deptno not in (select deptno from dept where dept.deptno is not null)


SQL Cookbook—查询、排序

涉及到的问题

1、在select语句中使用条件逻辑

2、限制返回的行数

3、从表中随机返回n条记录

4、将空值转换为实际值

5、对字母和数字混合的数据排序

6、处理排序空值

7、根据数据项的键排序

–8、从一个表中查找另一个表没有的值

–9、在一个表中查找与其他表不匹配的记录

–10、向查询中增加联接而不影响其他联接

–11、检测两个表中是否有相同的数据

–12、从多个表中返回丢失的数据

–13、在运算和比较时使用null值

–1、在select语句中使用条件逻辑

select ename,

sal,

case when sal<=2000 then 'UNDERPAID'

when sal>=4000 then 'OVERPAID'

else 'OK'

end as status

from emp

–2、限制返回的行数

select * from emp where rownum<=5

–3、从表中随机返回n条记录

select * from (

select ename, job from emp order by dbms_random.value()

)

where rownum <= 5

–4、将空值转换为实际值

–方法一

select comm, nvl(comm, 0) comm from emp

–方法二

select comm, coalesce(comm, 0) from emp

–方法三

select comm,

case when comm is null then 0

else comm

end comm

from emp

–5、对字母和数字混合的数据排序

–问题:现有字母和数字混合的数据,希望按照数字或字母部分来排序。考虑这个视图:

create view v_tt as

select ename||' '||deptno data from emp;

主要是通过replace和translate来实现

//by deptno(对数字排序)

select * from v_tt order by replace(data, replace(translate(data, '0123456789', '##########'), '#', ''), '');

//by ename(对字母排序)

select * from v_tt order by replace(translate(data, '0123456789', '##########'), '#', '');

补充:translate函数用法

select translate('123abc','2dc','4e') from dual;

因为from_string和to_string的位置是一一对应的,2对应4,d对应e,c没有对应的值,所以c应该会被删除。所以例子的字符里的2会替换为4,d因为字符串里没有,所以不作替换,c由于没有对应的替换字符,所以字符串里的c会被删除,那么可以得出,结果是143ab

–6、处理排序空值

主要方法是通过使用CASE表达式来“标记”一个值是否为NULL。这里标记有两个值,一个表示NULL,一个表示非NULL。这样,只要在ORDER BY子句中增加标记列,便可以很容易的控制空值是排在前面还是排在后面,而不会被空值所干扰。

//非空值按升序排序,空值排最后

select ename,sal,comm from(

select ename,sal,comm,

case when comm is null then 0 else 1 end as is_null

from emp

) x order by is_null desc,comm

//非空值按降序排序,空值排最后

select ename,sal,comm from(

select ename,sal,comm,

case when comm is null then 0 else 1 end as is_null

from emp

) x order by is_null desc,comm desc

//非空值按升序排序,空值排最前面

select ename,sal,comm from(

select ename,sal,comm,

case when comm is null then 0 else 1 end as is_null

from emp

) x order by is_null,comm

//非空值按降序排序,空值排最前面

select ename,sal,comm from(

select ename,sal,comm,

case when comm is null then 0 else 1 end as is_null

from emp

) x order by is_null,comm desc

在ORACLE中还可以使用NULLS FIRST和NULLS LAST来实现如上功能

//非空值按升序排序,空值排最后

select ename,sal,comm

from emp

order by comm nulls last

//非空值按降序排序,空值排最后

select ename,sal,comm

from emp

order by comm desc nulls last

//非空值按升序排序,空值排最前面

select ename,sal,comm

from emp

order by comm nulls first

//非空值按降序排序,空值排最前面

select ename,sal,comm

from emp

order by comm desc nulls first

–7、根据数据项的键排序

要根据某些条件逻辑来排序。例如,如果JOB是SALESMAN,要根据COMM来排序。否则,根据SAL排序。

select ename,sal,job,comm from emp

order by case when job ='SALSEMAN' then comm else sal end

–8、从一个表中查找另一个表没有的值

问题:从表dept中查找在表emp中不存在的数据的所有部门。示例数据中deptno的值在emp中不存在。

方法一:

select deptno from dept

minus

select deptno from emp

方法二:

select deptno from dept where deptno not in (select deptno from emp where deptno is not null)

注意:

1)、oracle中not in如果返回的有null值的话,不会返回记录。

例如:select deptno from dept where deptno not in (10, 20, null)

2)、在sql中,true or null的结果是true,而false or null的结果是null,所以在使用in和or计算时,值可能是null的情况,这一点要记住。

要解决not in这样问题,可以使用not exists和相关子查询(推荐)

select deptno from dept d where not exists(select 'xx' from emp e where e.deptno = d.deptno)

–9、在一个表中查找与其他表不匹配的记录

问题:对于具有相同关键字的两个表,要在一个表中查找与另一个表中不匹配的行。例如,要查找没有职员的部门(emp为从表)。

select d.* from emp e, dept d where e.deptno(+)=d.deptno and e.deptno is null

–10、向查询中增加联接而不影响其他联接

例如,要获得所有的员工信息、他们的工作部门的地点以及所获得的奖励

select e.ename, d.loc, eb.received

from emp e, dept d, emp_bonus eb

where e.deptno = d.deptno and e.empno = eb.empno

这样的查询结果,如果员工没有奖金,则无法显示该员工的信息,那么,无论有无奖金都要显示员工信息,就要使用到外连接

select e.ename, d.loc, eb.received

from emp e, dept d, emp_bonus eb

where e.deptno = d.deptno and e.empno = eb.empno(+)

order by 2

–11、检测两个表中是否有相同的数据

create view v2

as select * from scott.emp where deptno!=10

union all

select * from scott.emp where ename=upper('ward')

原理:

1)、首先,查找出表emp中存在而视图v2中没有的行。

2)、然后,合并在视图v2中存在,而在表emp中没有的行。

(select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt

from v2

group by empno,ename,job,mgr,hiredate,sal,comm,deptno

minus

select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt

from scott.emp

group by empno,ename,job,mgr,hiredate,sal,comm,deptno)

union all

(select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt

from scott.emp

group by empno,ename,job,mgr,hiredate,sal,comm,deptno

minus

select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt

from v2

group by empno,ename,job,mgr,hiredate,sal,comm,deptno)

–12、从多个表中返回丢失的数据

select d.deptno,d.dname,e.ename from scott.dept d,scott.emp e

where d.deptno=e.deptno(+)

union

select d.deptno,d.dname,e.ename from scott.dept d,scott.emp e

where d.deptno(+)=e.deptno

–13、在运算和比较时使用null值

select ename, comm from scott.emp where coalesce(comm,0) < (select comm from scott.emp where ename=upper('ward'));


SQL Cookbook—数字、日期

1、计算不包含最大值和最小值的均值

2、把字母数字串转换为数值

3、更改累计和中的值–显示存款或取款后的值

4、加减日、月、年

5、计算两个日期之间的天数

6、确定两个日期之间的工作日数目

表EMP中,计算BLAKE和JONES的hiredate(聘用日期)之间的工作日数(除去星期六、星期天)

7、确定两个日期之间的月份数或年数

例如:EMP表中,求第一个员工和最后一个员工之间相差的月份数,以及这些月折合的年数

8、确定两个日期之间的秒、分、小时数

例如:EMP表中,求ALLEN和WARD的hiredate(聘用日期)之间相差的时间,分别用秒、分、小时表示

9、计算一年中周内各日期的次数

10、确定当前记录和下一条记录之间相差的天数

11、确定一年是否为闰年

12、确定一年内的天数

13、从日期中提取时间的各个部分

14、确定某个月的第一天和最后一天

15、列出当年中的所有所于星期五的日期

16、确定某月内第一个和最后一个“周内某天”的日期

例如:找出当前月的第一个星期一及最后一个星期一的日期 next_day下一个星期几

17、列出一年中每个季度的开始日期和结束日期

18、确定某个给定季度的开始日期和结束日期

19、填充丢失的日期

–为给定范围内的每个日期(每个月、周或年)生成一行信息,这样的行集通常用于生成综合报告。

–例如,计算每年内每个月聘用的员工数。检查已聘用的所有员工的聘用日期,其范围是 1980-1983

–现在,要确定1980-1983年间每个月聘用的员工数。如有哪个月没有聘用,则显示为0个

20、按照给定的时间单位进行查找

–问题:查找与给定月份、星期几或其他时间单位相匹配的日期。

–例如:找到2月份和12月份聘用的所有员工或者查找星期二聘用的所有员工

21、使用日期的特殊部分比较记录

–问题:查找聘用日期月份和周内日期都相同的员工。例如,如果在1988年3月10日星期一聘用了某了员工,

–而在2001年3月2日星期一聘用了另一个员工,那么,由于二者的聘用日期都在星期一,而且月份名一致,则可以认为他们相匹配

22、识别重叠的日期范围

–问题:查找员工在老工程结束之前就开始新工作的所有实例 当结束那天正好接了另一个项目也算

–一个开始时间在 另一行记录的开始时间和结束时间之间
1、计算不包含最大值和最小值的均值

SELECT (SUM(ID)-MAX(ID)-MIN(ID))/(COUNT(1)-2) FROM tb_dict
2、把字母数字串转换为数值

SELECT REPLACE(TRANSLATE(LOWER('adc12s3as'),

LOWER('abcdefghijklmnopqrstuvwxyz'),

RPAD('z', 24, 'z')),

'z',

'')

FROM DUAL
3、更改累计和中的值–显示存款或取款后的值

with tmp as (

select 1 tid,100 atm,'存款' trx from dual union all

select 2 tid,100 atm,'存款' trx from dual union all

select 3 tid,50 atm,'取款' trx from dual union all

select 4 tid,100 atm,'存款' trx from dual union all

select 5 tid,200 atm,'取款' trx from dual union all

select 6 tid,50 atm,'取款' trx from dual

)

select trx,atm,sum(decode(trx,'存款',atm,-atm)) over (order by tid) balance from tmp
4、加减日、月、年

SELECT HIREDATE,

HIREDATE – 5 AS HD_MINUS_5D,–减日

HIREDATE + 5 AS HD_PLUS_5D,–加日

ADD_MONTHS(HIREDATE, -5) AS HD_MINUS_5M,–减月

ADD_MONTHS(HIREDATE, 5) AS HD_PLUS_5M,–加月

ADD_MONTHS(HIREDATE, -5 * 12) AS HD_MINUS_5Y,–减年

ADD_MONTHS(HIREDATE, 5 * 12) AS HD_PLUS_5Y–加年

FROM EMP

WHERE DEPTNO = 10
5、计算两个日期之间的天数

SELECT to_date('2012-10-22', 'yyyy-mm-dd') – to_date('2012-9-22', 'yyyy-mm-dd') FROM dual;
6、确定两个日期之间的工作日数目

表EMP中,计算BLAKE和JONES的hiredate(聘用日期)之间的工作日数(除去星期六、星期天)

步骤:

1)、建立索引表

create table t500(

id number(3)

)

2)、插入索引数据

declare

v_index number(3);

begin

for v_index in 1..500 loop

insert into t500 values(v_index);

end loop;

end;

3)、查询语句ss

SELECT SUM(CASE

WHEN TO_CHAR(JONES_HIREDATE + T500.ID – 1, 'DY') IN('星期六', '星期日') THEN 0 ELSE 1

END) AS DAYS

FROM (SELECT MAX(CASE WHEN ENAME = 'BLAKE' THEN HIREDATE END) AS BLAKE_HIREDATE,

MAX(CASE WHEN ENAME = 'JONES' THEN HIREDATE END) AS JONES_HIREDATE

FROM EMP

WHERE ENAME IN ('BLAKE', 'JONES')) X,

T500

WHERE T500.ID <= BLAKE_HIREDATE – JONES_HIREDATE + 1
7、确定两个日期之间的月份数或年数

例如:EMP表中,求第一个员工和最后一个员工之间相差的月份数,以及这些月折合的年数

SELECT MONTHS_BETWEEN(MAX_HIREDATE, MIN_HIREDATE),

MONTHS_BETWEEN(MAX_HIREDATE, MIN_HIREDATE) / 12

FROM (SELECT MIN(HIREDATE) MIN_HIREDATE, MAX(HIREDATE) MAX_HIREDATE

FROM EMP)
8、确定两个日期之间的秒、分、小时数

例如:EMP表中,求ALLEN和WARD的hiredate(聘用日期)之间相差的时间,分别用秒、分、小时表示

select dy*24 as hr, dy*24*60 as min, dy*24*60*60 as sec

from (select (max(case when ename='WARD' then hiredate end)-max(case when ename='ALLEN' then hiredate end)) as dy from emp)
9、计算一年中周内各日期的次数

SELECT TO_CHAR(TRUNC(SYSDATE, 'y') + ROWNUM – 1, 'DY'), COUNT(*)

FROM T500

WHERE ROWNUM <= ADD_MONTHS(TRUNC(SYSDATE, 'y'), 12) – TRUNC(SYSDATE, 'y')

GROUP BY TO_CHAR(TRUNC(SYSDATE, 'y') + ROWNUM – 1, 'DY')
10、确定当前记录和下一条记录之间相差的天数

SELECT ENAME, HIREDATE, NEXT_HD, NEXT_HD – HIREDATE DIFF

FROM (SELECT DEPTNO,

ENAME,

HIREDATE,

LEAD(HIREDATE) OVER(ORDER BY HIREDATE) NEXT_HD

FROM EMP)

WHERE DEPTNO = 10
11、确定一年是否为闰年

此处采用了最简单的方案,检查2月最后一天,如果是29,则当年就为闰年,即此处的关键是得到2月的最后一天

select to_char(last_day(add_months(trunc(sysdate, 'y'), 1)),'DD') from dual
12、确定一年内的天数

select add_months(trunc(sysdate, 'y'), 12)-trunc(sysdate, 'y') from dual
13、从日期中提取时间的各个部分

SELECT TO_CHAR(SYSDATE, 'hh24') HOUR,

TO_CHAR(SYSDATE, 'mi') MIN,

TO_CHAR(SYSDATE, 'ss') SEC,

TO_CHAR(SYSDATE, 'dd') DAY,

TO_CHAR(SYSDATE, 'mm') MONTH,

TO_CHAR(SYSDATE, 'yyyy') YEAR

FROM DUAL
14、确定某个月的第一天和最后一天

SELECT TRUNC(SYSDATE, 'mm') FIRSTDAY, LAST_DAY(SYSDATE) LASTDAY FROM DUAL
15、列出当年中的所有所于星期五的日期

with tmp_a as(

–获取每一天

select level num,trunc(sysdate,'y')+level-1 everyday from dual

connect by level <= add_months(trunc(sysdate,'yyyy'),12)-trunc(sysdate,'y'))

select num,everyday,to_char(everyday,'day')

from tmp_a

where to_char(everyday,'day') = '星期五'
16、确定某月内第一个和最后一个“周内某天”的日期

例如:找出当前月的第一个星期一及最后一个星期一的日期 next_day下一个星期几

–方法一

with tmp_a as (

select level num,trunc(sysdate,'mm')+level-1 everyday from dual

connect by level <= last_day(sysdate)-trunc(sysdate,'mm')+1)

select min(everyday) 第一个星期一,

max(everyday) 最后一个星期一 from tmp_a

where to_char(everyday,'day') = '星期一'

–方法二

select next_day(trunc(sysdate,'mm')-1,'星期一'),

next_day(last_day(trunc(sysdate,'mm')),'星期一')-7

from dual
17、列出一年中每个季度的开始日期和结束日期

with tmp_a as(

select ddate,to_char(ddate,'q') jidu from (

select level,trunc(sysdate,'y')+level-1 ddate from dual

connect by level <= add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y'))

)select jidu 季度,min(ddate) 开始日期,max(ddate) 结束日期 from tmp_a

group by jidu
18、确定某个给定季度的开始日期和结束日期

–方法一

with tmp_a as(

select ddate,to_char(ddate,'q') jidu from (

select level,trunc(sysdate,'y')+level-1 ddate from dual

connect by level <= add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y'))

)

select min(ddate),max(ddate) from tmp_a

where jidu = '4'

–方法二

select jidu,add_months(ddate,-2) 开始日期,last_day(ddate) 结束日期 from (

select jidu,to_date(ye||lpad(mon,2,'0'),'yyyymm') ddate from (

select jidu,substr(jidu,1,4) ye,mod(jidu,10)*3 mon from (

select 20081 jidu from dual union all

select 20082 from dual union all

select 20083 from dual union all

select 20084 from dual

)))
19、填充丢失的日期

–为给定范围内的每个日期(每个月、周或年)生成一行信息,这样的行集通常用于生成综合报告。

–例如,计算每年内每个月聘用的员工数。检查已聘用的所有员工的聘用日期,其范围是 1980-1983

–现在,要确定1980-1983年间每个月聘用的员工数。如有哪个月没有聘用,则显示为0个

with tmp_a as(

select 'aa' tno,to_date('19800506','yyyymmdd') hiredate from dual union all

select 'bb' tno,to_date('19830701','yyyymmdd') hiredate from dual union all

select 'cc' tno,to_date('19860702','yyyymmdd') hiredate from dual union all

select 'dd' tno,to_date('19810510','yyyymmdd') hiredate from dual union all

select 'ee' tno,to_date('19820503','yyyymmdd') hiredate from dual union all

select 'gg' tno,to_date('19820504','yyyymmdd') hiredate from dual union all

select 'ff' tno,to_date('19821015','yyyymmdd') hiredate from dual

)

select sta,sum(cnt) cnts,count(hiredate) from (–聚合函数count()不会计算为空的列,所以不必把null转换,在下面一层就可以求出来

select sta,tno,hiredate,decode(tno,null,0,1) cnt from (

select level,add_months(trunc(min_date,'y'),level-1) sta from (

select min(hiredate) min_date,max(hiredate) max_date from tmp_a

)

connect by level <= months_between(add_months(trunc(max_date,'y'),12),trunc(min_date,'y')) ) t

left join tmp_a on sta = trunc(hiredate,'mm')

)group by sta

order by sta
20、按照给定的时间单位进行查找

–问题:查找与给定月份、星期几或其他时间单位相匹配的日期。

–例如:找到2月份和12月份聘用的所有员工或者查找星期二聘用的所有员工

with tmp_a as(

select 'aa' tno,to_date('19800506','yyyymmdd') hiredate from dual union all

select 'bb' tno,to_date('19830701','yyyymmdd') hiredate from dual union all

select 'cc' tno,to_date('19860702','yyyymmdd') hiredate from dual union all

select 'dd' tno,to_date('19810510','yyyymmdd') hiredate from dual union all

select 'ee' tno,to_date('19820503','yyyymmdd') hiredate from dual union all

select 'gg' tno,to_date('19820504','yyyymmdd') hiredate from dual union all

select 'ff' tno,to_date('19821015','yyyymmdd') hiredate from dual

)

select tno,hiredate,to_char(hiredate,'day'),to_char(hiredate,'mm') from tmp_a

where to_char(hiredate,'day') = '星期二' or to_char(hiredate,'mm') in('07','05')
21、使用日期的特殊部分比较记录

–问题:查找聘用日期月份和周内日期都相同的员工。例如,如果在1988年3月10日星期一聘用了某了员工,

–而在2001年3月2日星期一聘用了另一个员工,那么,由于二者的聘用日期都在星期一,而且月份名一致,则可以认为他们相匹配

with tmp_a as(

select tno,hiredate,to_char(hiredate,'day') xingqi,to_char(hiredate,'mm') mon from (

select 'aa' tno,to_date('19800506','yyyymmdd') hiredate from dual union all

select 'bb' tno,to_date('19830701','yyyymmdd') hiredate from dual union all

select 'cc' tno,to_date('19860702','yyyymmdd') hiredate from dual union all

select 'dd' tno,to_date('19810510','yyyymmdd') hiredate from dual union all

select 'ee' tno,to_date('19820503','yyyymmdd') hiredate from dual union all

select 'tt' tno,to_date('20090512','yyyymmdd') hiredate from dual union all

select 'gg' tno,to_date('19820504','yyyymmdd') hiredate from dual union all

select 'ff' tno,to_date('19821015','yyyymmdd') hiredate from dual)

)

select * from tmp_a x,tmp_a y

where x.xingqi = y.xingqi and x.mon = y.mon and x.tno > y.tno
22、识别重叠的日期范围

–问题:查找员工在老工程结束之前就开始新工作的所有实例 当结束那天正好接了另一个项目也算

–一个开始时间在 另一行记录的开始时间和结束时间之间

–方法一

with tmp_a as(

select 7782 empno,'clark' ename,1 proj_id,to_date('20050616','yyyymmdd') proj_start,to_date('20050618','yyyymmdd') proj_end from dual union all

select 7782 empno,'clark' ename,4 proj_id,to_date('20050619','yyyymmdd') proj_start,to_date('20050624','yyyymmdd') proj_end from dual union all

select 7782 empno,'clark' ename,7 proj_id,to_date('20050622','yyyymmdd') proj_start,to_date('20050625','yyyymmdd') proj_end from dual union all

select 7782 empno,'clark' ename,10 proj_id,to_date('20050625','yyyymmdd') proj_start,to_date('20050628','yyyymmdd') proj_end from dual union all

select 7782 empno,'clark' ename,13 proj_id,to_date('20050628','yyyymmdd') proj_start,to_date('20050702','yyyymmdd') proj_end from dual union all

select 7839 empno,'king' ename,2 proj_id,to_date('20050617','yyyymmdd') proj_start,to_date('20050621','yyyymmdd') proj_end from dual union all

select 7839 empno,'king' ename,8 proj_id,to_date('20050623','yyyymmdd') proj_start,to_date('20050625','yyyymmdd') proj_end from dual union all

select 7839 empno,'king' ename,14 proj_id,to_date('20050629','yyyymmdd') proj_start,to_date('20050630','yyyymmdd') proj_end from dual union all

select 7839 empno,'king' ename,11 proj_id,to_date('20050626','yyyymmdd') proj_start,to_date('20050627','yyyymmdd') proj_end from dual union all

select 7839 empno,'king' ename,5 proj_id,to_date('20050620','yyyymmdd') proj_start,to_date('20050624','yyyymmdd') proj_end from dual union all

select 7934 empno,'miller' ename,3 proj_id,to_date('20050618','yyyymmdd') proj_start,to_date('20050622','yyyymmdd') proj_end from dual union all

select 7934 empno,'miller' ename,12 proj_id,to_date('20050627','yyyymmdd') proj_start,to_date('20050628','yyyymmdd') proj_end from dual union all

select 7934 empno,'miller' ename,15 proj_id,to_date('20050630','yyyymmdd') proj_start,to_date('20050703','yyyymmdd') proj_end from dual union all

select 7934 empno,'miller' ename,9 proj_id,to_date('20050624','yyyymmdd') proj_start,to_date('20050627','yyyymmdd') proj_end from dual union all

select 7934 empno,'miller' ename,6 proj_id,to_date('20050621','yyyymmdd') proj_start,to_date('20050623','yyyymmdd') proj_end from dual

)select * from tmp_a x,tmp_a y

where x.ename = y.ename and x.proj_start <= y.proj_end and x.proj_id <> y.proj_id

and x.proj_start >= y.proj_start
–方法二

–从1开始找出不连续的值 用联接 先产生一批连续的值,与所求表去联接则可

with tmp_a as(

select level base from dual

connect by level <= 100000

)select * from tmp_a x,(

select 2 num from dual union all

select 5 num from dual union all

select 6 num from dual union all

select 9 num from dual union all

select 12 num from dual

)y where x.base = y.num(+)

and y.num is null

order by x.base


oracle insert两个关联表

现有一张老师学生表(tb_tea_cou),由于业务需要,需把老师学生表tb_tea_stu拆分成两张表(tb_tea、tb_cou),并把记录insert到这两张子表中(tb_tea、tb_cou为关联的两张表)。
表结构如下:

tb_tea_cou(

id, //pk

name, //任课老师

zc, //职称

course //课程

),
老师表:

tb_tea(

tid, //pk

tname, //任课老师

zc //职称

),
课程表:

tb_cou(

cid, //pk

course, //课程

tea_id //fk,tb_tea id

)
插入数据

INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '张三', '教师', '语文');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '李四', '教师', '数学');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '王五', '教师', '英语');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '老刘', '教师', '历史');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '小王', '教师', '政治');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '杜甫', '教师', '生物');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '李白', '教师', '化学');
INSERT INTO tb_tea_cou (ID, name, zc, course) VALUES (hibernate_seq.nextval, '韩愈', '教师', '物理');
select * from tb_tea_cou;


思路,使用存储过程,插入tb_tea表之后,select max(tid) from tb_tea; 得到刚刚插入的序列的最大值,在后面将这个值插入tb_cou表中。

--创建存储过程
SET serveroutput ON;
CREATE OR REPLACE PROCEDURE proce_insert_tab(error_msg OUT VARCHAR2) IS
v_id NUMBER(9, 2);
BEGIN
FOR cur IN (select * FROM tb_tea_cou) LOOP
SELECT hibernate_seq.nextval INTO v_id FROM dual;
INSERT INTO tb_tea(tid, tname, zc) VALUES (
v_id, cur.name, cur.zc);
INSERT INTO tb_cou(cid, course, tea_id) VALUES (
hibernate_seq.nextval, cur.course, v_id);
COMMIT;
error_msg:='添加成功';
END LOOP;
EXCEPTION
WHEN OTHERS THEN ROLLBACK;
error_msg:='添加失败';
RAISE_APPLICATION_ERROR(-20010, 'ERROR:插入数据有误!');
END;
/

--调用存储过程
var error_msg VARCHAR2(200);
exec proce_insert_tab(:error_msg);

SELECT * FROM tb_tea;
SELECT * FROM tb_cou;
SELECT t.tid, t.tname, t.zc, c.course FROM tb_tea t, tb_cou c WHERE t.tid=c.tea_id
delete from tb_tea;
delete from tb_cou;



oracle sqlplus命令详解

涉及到的知识要点

a、带有一个&的替换变量的用法

b、带有两个&的替换变量用法

c、define命令用法

d、accept命令用法

e、定制SQL*Plus环境

f、在glogin.sql文件中保存定制结果

g、sqlplus编辑命令

a、带有一个&的替换变量的用法

1)、使用带有一个&号的变量值来提示用户输入一个值。

eg、

SQL> SELECT empno, ename, sal, deptno FROM emp WHERE empno = &empno;

输入 empno 的值: 7369

原值 1: SELECT empno, ename, sal, deptno FROM emp WHERE empno = &empno

新值 1: SELECT empno, ename, sal, deptno FROM emp WHERE empno = 7369
EMPNO ENAME SAL DEPTNO

———- ———- ———- ———-

7369 SMITH 8888 20
2)、替换变量中的字串和日期值

使用单引号标志替换变量中的日期和字串值。

eg、

SQL> SELECT ename, deptno, sal*12 FROM emp WHERE job='&job';

输入 job 的值: ANALYST

原值 1: SELECT ename, deptno, sal*12 FROM emp WHERE job='&job'

新值 1: SELECT ename, deptno, sal*12 FROM emp WHERE job='ANALYST'
ENAME DEPTNO SAL*12

———- ———- ———-

SCOTT 20 106656

FORD 20 36000
3)、运行时指定列名、表达式、文本

SELECT empno, ename, job, &column_name

FROM emp

WHERE &condition

ORDER BY &order_column;

输入 column_name 的值: sal

原值 1: SELECT empno, ename, job, &column_name

新值 1: SELECT empno, ename, job, sal

输入 condition 的值: sal>=3000

原值 3: WHERE &condition

新值 3: WHERE sal>=3000

输入 order_column 的值: ename

原值 4: ORDER BY &order_column

新值 4: ORDER BY ename
EMPNO ENAME JOB SAL

———- ———- ——— ———-

7902 FORD ANALYST 3000

7839 KING PRESIDENT 5000

7788 SCOTT ANALYST 8888

7369 SMITH CLERK 8888
b、带有两个&的替换变量用法

使用带有两个&的变量可以使得该变量可以重复使用,而不必在每次使用时提醒用户输入。

SQL> SELECT empno, ename, job, &&column_name FROM emp ORDER BY &column_name;

输入 column_name 的值: deptno

原值 1: SELECT empno, ename, job, &&column_name FROM emp ORDER BY &column_name

新值 1: SELECT empno, ename, job, deptno FROM emp ORDER BY deptno
EMPNO ENAME JOB DEPTNO

———- ———- ——— ———-

7782 CLARK MANAGER 10

7839 KING PRESIDENT 10

7934 MILLER CLERK 10


c、define命令用法

创建CHAR类型的用户变量,当定义一个包含空格的变量时,要用单引号将该变量括起来。

eg、

SQL> define deptname = sales

SQL> define deptname; –查看该变量

DEFINE DEPTNAME = "sales" (CHAR)

SQL> SELECT * FROM dept WHERE dname=UPPER('&deptname');

原值 1: SELECT * FROM dept WHERE dname=UPPER('&deptname')

新值 1: SELECT * FROM dept WHERE dname=UPPER('sales')
DEPTNO DNAME LOC

———- ————– ————-

30 SALES CHICAGO
说明:一个变量将保持在被定义的状态,直到使用undefine命令将它清除或离开sql*plus。
d、accept命令用法

接收用户输入时,创建可定制的用户提示。

eg、

–提示你输入一个数值给dept,dept是个变量,可用define命令查看该变量

SQL> accept dept prompt 'Provide the department name: '

Provide the department name: Sales

SQL> define dept; –查看该变量

DEFINE DEPT = "Sales" (CHAR)

SQL> SELECT * FROM dept WHERE dname = UPPER('&dept');

原值 1: SELECT * FROM dept WHERE dname = UPPER('&dept')

新值 1: SELECT * FROM dept WHERE dname = UPPER('Sales')
DEPTNO DNAME LOC

———- ————– ————-

30 SALES CHICAGO
e、定制SQL*Plus环境

使用SET命令来控制当前的会话。

语法:SET system_variable value

eg、SQL> set arraysize 20;

可以使用SHOW命令来查看set设置。

eg、SQL> show arraysize

arraysize 20

说明: set命令用法具体请看” oracle set命令详解.txt”
f、在glogin.sql文件中保存定制结果

glogin.sql文件包含在登录时需要执行的标准SET命令及其他命令,我们可以更改glogin.sql以包含其他SET命令。

如:设置sqlplus环境的linesize为300,并让这个变量永久生效在$ORACLE_HOME/sqlplus/admin/glogin.sql(D:\dev\oracle\product\10.2.0\db_1\sqlplus\admin)文件中添加如下内容:set
linesize 300

a

g、sqlplus编辑命令

1)、执行def命令查看编辑器的设置:

SQL> def
2)、总结下:

l–列sql

n–切换活动行(n代表行数字)

a–活动行后增加(append)

i–活动行后插入新行增加(input)

c–替换(change)

eg、c /emp_name/emp_age/

del n–删除行n

/–执行sql

eg、

SQL> select deptno, dname from dept;
DEPTNO DNAME

———- ————–

10 ACCOUNTING

20 RESEARCH

30 SALES

40 OPERATIONS
SQL> i where deptno>20

SQL> l

1 select deptno, dname from dept

2* where deptno>20

SQL> /
DEPTNO DNAME

———- ————–

30 SALES

40 OPERATIONS
SQL> del 2

SQL> l

1* select deptno, dname from dept

SQL> a where deptno>30

1* select deptno, dname from dept where deptno>30

SQL> l

1* select deptno, dname from dept where deptno>30

SQL> /
DEPTNO DNAME

———- ————–

40 OPERATIONS


sqlplus column命令用法

column是sqlplus里最实用的一个命令,很多时候sql语句输出的列宽度不合适而影响查看,都需要用到这个命令来更改select语句中指定列的宽度和标题。大部分时候,我们可以简写column为col即可,主要有以下用法:

a)、修改列宽度

col c1 format a20 –将列c1(字符型)显示最大宽度调整为20个字符

col c1 format 9999999 –将列c1(number型)显示最大宽度调整为7个字符

b)、修改列标题

col c1 heading c2 –将c1的列名输出为c2

c)、设置列的对齐方式

SQL> col ename justify left/right/center;

SQL> select empno, ename, job from emp;

注意:对于number类型的数据默认为右对齐,其他默认为左对齐

d)、隐藏某列显示:col job noprint

SQL> col job noprint;

SQL> select empno, ename, job from emp;

e)、格式化number类型列的显示:column sal format $999,999.00

SQL> column sal format $999,999.00

SQL> select empno, ename, sal from emp;

e)、设置列值,若列植为空以text代替

SQL> col comm null text

SQL> select * from emp;

f)、显示列的当前属性

SQL> column ename;

g)、重置为默认值:

SQL> clear columns;

h)、一行只显示数字位的长度, 超过长度折行, 加word_wrapped后, 单词不会折行

column info format a40 word_wrapped

i)、cle[ar]: 清除掉所有的列格式

j)、设置列头

sql> column ename heading '姓名' format a15


oracle set命令详解

SQL>set colsep '|'; //输出分隔符

eg、

SQL> set colsep '|';

SQL> select * from dept;
DEPTNO|DNAME |LOC

———-|————–|————-

10|ACCOUNTING |NEW YORK

20|RESEARCH |DALLAS

30|SALES |CHICAGO

40|OPERATIONS |BOSTON
SQL>set echo off; //显示start启动的脚本中的每个sql命令,缺省为on

SQL>set echo on; //设置运行命令是否显示语句

eg、

SQL> set echo on;

SQL> start d:/log.sql;

SQL> select * from dept

2 /
DEPTNO|DNAME |LOC

———-|————–|————-

10|ACCOUNTING |NEW YORK

20|RESEARCH |DALLAS

30|SALES |CHICAGO

40|OPERATIONS |BOSTON
SQL>set feedback on; //设置显示“已选择XX行”

SQL>set feedback off; //回显本次sql命令处理的记录条数,缺省为on

SQL>set heading on; //输出字段标题,缺省为on

SQL>set pagesize 0; //输出每页行数,缺省为24,为了避免分页,可设定为0。

SQL>set linesize 80; //输出一行字符个数,缺省为80

SQL>set numwidth 12; //输出number类型长度,缺省为10

SQL>set termout off; //显示脚本中的命令的执行结果,缺省为on

SQL>set trimout on; //去除标准输出每行的拖尾空格,缺省为off

SQL>set trimspool on; //去除重定向(spool)输出每行的拖尾空格,缺省为off

SQL>set serveroutput on; //设置允许显示输出类似dbms_output

SQL>set timing on; //设置显示“已用时间:XXXX”

SQL>set autotrace on; //设置允许对执行的sql进行分析

SQL>set verify off; //可以关闭和打开提示确认信息old 1和new 1的显示.

eg、

SQL> SET VERIFY ON;

SQL> SELECT empno, ename, sal, deptno FROM emp WHERE empno = &employee_num;

输入 employee_num 的值: 7369

原值 1: SELECT empno, ename, sal, deptno FROM emp WHERE empno = &employ

新值 1: SELECT empno, ename, sal, deptno FROM emp WHERE empno = 7369
DEPTNO|DNAME |LOC

———-|————–|————-

7369|SMITH | 8888| 20
SQL> show arraysize

SQL> set arraysize 20

它表示从Oracle服务器端一次只传递15行记录到客户端(SQLPLUS),SQLPLUS中arraysize默认为15。
SQL> show long //设置显示long,lob等型字段的长度,默认为80

SQL> set long 80
SQL> show PAUSE //设置滚屏是否自动

SQL> set PAUSE {OFF | ON | text}


Oracle 行列转换总结

行列转换包括以下六种情况:

*列转行

*行转列

*多列转换成字符串

*多行转换成字符串

*字符串转换成多列

*字符串转换成多行
下面分别进行举例介绍。
首先声明一点,有些例子需要如下10g及以后才有的知识:

a、掌握model子句

b、正则表达式

c、加强的层次查询
讨论的适用范围只包括8i,9i,10g及以后版本。begin:
1、列转行
未列转行之前的效果如下:



列转行的效果如下:



sql代码:

CREATE TABLE t_col_row(
ID INT,
c1 VARCHAR2(10),
c2 VARCHAR2(10),
c3 VARCHAR2(10)
);

INSERT INTO t_col_row VALUES (1, 'v11', 'v21', 'v31');
INSERT INTO t_col_row VALUES (2, 'v12', 'v22', NULL);
INSERT INTO t_col_row VALUES (3, 'v13', NULL, 'v33');
INSERT INTO t_col_row VALUES (4, NULL, 'v24', 'v34');
INSERT INTO t_col_row VALUES (5, 'v15', NULL, NULL);
INSERT INTO t_col_row VALUES (6, NULL, NULL, 'v35');
INSERT INTO t_col_row VALUES (7, NULL, NULL, NULL);
COMMIT;

SELECT * FROM t_col_row;


1).UNION ALL–>适用范围:8i,9i,10g及以后版本

sql代码:

SELECT id, 'c1' cn, c1 cv
FROM t_col_row
UNION ALL
SELECT id, 'c2' cn, c2 cv
FROM t_col_row
UNION ALL
SELECT id, 'c3' cn, c3 cv FROM t_col_row;


若空行不需要转换,只需加一个where条件,

sql代码:

WHERE COLUMN IS NOT NULL


2).MODEL–>适用范围:10g及以后

SELECT id, cn, cv FROM t_col_row
MODEL
RETURN UPDATED ROWS
PARTITION BY (ID)
DIMENSION BY (0 AS n)
MEASURES ('xx' AS cn,'yyy' AS cv,c1,c2,c3) --xx、yyy表示字段长度
RULES UPSERT ALL
(
cn[1] = 'c1',
cn[2] = 'c2',
cn[3] = 'c3',
cv[1] = c1[0],
cv[2] = c2[0],
cv[3] = c3[0]
)
ORDER BY ID,cn;


现在小分析一下上面这个查询:

partition by (prd_type_id)指定结果是根据prd_type_id分区的。

dimension by (0 AS n)定义数组的长度,这就意味着必须提供数组索引才能访问数组中的单元。

measures ('xx' AS cn)表明数组中的每个单元包含一个数量,同时表明数组名为cn。
3).collection–>适用范围:8i,9i,10g及以后版本

要创建一个对象和一个集合:

sql语句:

CREATE TYPE cv_pair AS OBJECT(cn VARCHAR2(10), cv VARCHAR2(10));
CREATE TYPE cv_varr AS VARRAY(8) OF cv_pair;

SELECT id, t.cn AS cn, t.cv AS cv
FROM t_col_row,
TABLE(cv_varr(cv_pair('c1', t_col_row.c1),
cv_pair('c2', t_col_row.c2),
cv_pair('c3', t_col_row.c3))) t
ORDER BY 1, 2;


2、行转列

未行转列之前的效果如下:



行转列的效果如下:



CREATE TABLE t_row_col AS
SELECT id, 'c1' cn, c1 cv FROM t_col_row UNION ALL SELECT id, 'c2' cn, c2 cv FROM t_col_row UNION ALL SELECT id, 'c3' cn, c3 cv FROM t_col_row;

SELECT * FROM t_row_col ORDER BY 1,2;


1)AGGREGATE FUNCTION–>适用范围:8i,9i,10g及以后版本

SELECT id,
MAX(decode(cn, 'c1', cv, NULL)) AS c1,
MAX(decode(cn, 'c2', cv, NULL)) AS c2,
MAX(decode(cn, 'c3', cv, NULL)) AS c3
FROM t_row_col
GROUP BY id
ORDER BY 1;


max聚集函数也可以用sum、min、avg等其他聚集函数替代。
被指定的转置列只能有一列,但固定的列可以有多列,请看下面的例子:

SELECT mgr, deptno, empno, ename FROM scott.emp ORDER BY 1, 2;

SELECT mgr,
deptno,
MAX(decode(empno, '7788', ename, NULL)) "7788",
MAX(decode(empno, '7902', ename, NULL)) "7902",
MAX(decode(empno, '7844', ename, NULL)) "7844",
MAX(decode(empno, '7521', ename, NULL)) "7521",
MAX(decode(empno, '7900', ename, NULL)) "7900",
MAX(decode(empno, '7499', ename, NULL)) "7499",
MAX(decode(empno, '7654', ename, NULL)) "7654"
FROM scott.emp
WHERE mgr IN (7566, 7698)
AND deptno IN (20, 30)
GROUP BY mgr, deptno
ORDER BY 1, 2;


这里转置列为empno,固定列为mgr,deptno。
还有一种行转列的方式,就是相同组中的行值变为单个列值,但转置的行值不变为列名:

SELECT id,
MAX(decode(rn, 1, cn, NULL)) cn_1,
MAX(decode(rn, 1, cv, NULL)) cv_1,
MAX(decode(rn, 2, cn, NULL)) cn_2,
MAX(decode(rn, 2, cv, NULL)) cv_2,
MAX(decode(rn, 3, cn, NULL)) cn_3,
MAX(decode(rn, 3, cv, NULL)) cv_3
FROM (SELECT id,
cn,
cv,
row_number() over(PARTITION BY id ORDER BY cn, cv) rn
FROM t_row_col)
GROUP BY ID;


结果效果如下:



2)PL/SQL

适用范围:8i,9i,10g及以后版本

这种对于行值不固定的情况可以使用。

下面是我写的一个包,包中

p_rows_column_real用于前述的第一种不限定列的转换;

p_rows_column用于前述的第二种不限定列的转换。

CREATE OR REPLACE PACKAGE pkg_dynamic_rows_column AS
TYPE refc IS REF CURSOR;

PROCEDURE p_print_sql(p_txt VARCHAR2);

FUNCTION f_split_str(p_str VARCHAR2, p_division VARCHAR2, p_seq INT)
RETURN VARCHAR2;

PROCEDURE p_rows_column(p_table      IN VARCHAR2,
p_keep_cols  IN VARCHAR2,
p_pivot_cols IN VARCHAR2,
p_where      IN VARCHAR2 DEFAULT NULL,
p_refc       IN OUT refc);

PROCEDURE p_rows_column_real(p_table     IN VARCHAR2,
p_keep_cols IN VARCHAR2,
p_pivot_col IN VARCHAR2,
p_pivot_val IN VARCHAR2,
p_where     IN VARCHAR2 DEFAULT NULL,
p_refc      IN OUT refc);
END;
/
CREATE OR REPLACE PACKAGE BODY pkg_dynamic_rows_column AS

PROCEDURE p_print_sql(p_txt VARCHAR2) IS
v_len INT;
BEGIN
v_len := length(p_txt);
FOR i IN 1 .. v_len / 250 + 1 LOOP
dbms_output.put_line(substrb(p_txt, (i - 1) * 250 + 1, 250));
END LOOP;
END;

FUNCTION f_split_str(p_str VARCHAR2, p_division VARCHAR2, p_seq INT)
RETURN VARCHAR2 IS
v_first INT;
v_last  INT;
BEGIN
IF p_seq < 1 THEN
RETURN NULL;
END IF;
IF p_seq = 1 THEN
IF instr(p_str, p_division, 1, p_seq) = 0 THEN
RETURN p_str;
ELSE
RETURN substr(p_str, 1, instr(p_str, p_division, 1) - 1);
END IF;
ELSE
v_first := instr(p_str, p_division, 1, p_seq - 1);
v_last  := instr(p_str, p_division, 1, p_seq);
IF (v_last = 0) THEN
IF (v_first > 0) THEN
RETURN substr(p_str, v_first + 1);
ELSE
RETURN NULL;
END IF;
ELSE
RETURN substr(p_str, v_first + 1, v_last - v_first - 1);
END IF;
END IF;
END f_split_str;

PROCEDURE p_rows_column(p_table      IN VARCHAR2,
p_keep_cols  IN VARCHAR2,
p_pivot_cols IN VARCHAR2,
p_where      IN VARCHAR2 DEFAULT NULL,
p_refc       IN OUT refc) IS
v_sql VARCHAR2(4000);
TYPE v_keep_ind_by IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
v_keep v_keep_ind_by;

TYPE v_pivot_ind_by IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
v_pivot v_pivot_ind_by;

v_keep_cnt   INT;
v_pivot_cnt  INT;
v_max_cols   INT;
v_partition  VARCHAR2(4000);
v_partition1 VARCHAR2(4000);
v_partition2 VARCHAR2(4000);
BEGIN
v_keep_cnt  := length(p_keep_cols) - length(REPLACE(p_keep_cols, ',')) + 1;
v_pivot_cnt := length(p_pivot_cols) -
length(REPLACE(p_pivot_cols, ',')) + 1;
FOR i IN 1 .. v_keep_cnt LOOP
v_keep(i) := f_split_str(p_keep_cols, ',', i);
END LOOP;
FOR j IN 1 .. v_pivot_cnt LOOP
v_pivot(j) := f_split_str(p_pivot_cols, ',', j);
END LOOP;
v_sql := 'select max(count(*)) from ' || p_table || ' group by ';
FOR i IN 1 .. v_keep.LAST LOOP
v_sql := v_sql || v_keep(i) || ',';
END LOOP;
v_sql := rtrim(v_sql, ',');
EXECUTE IMMEDIATE v_sql
INTO v_max_cols;
v_partition := 'select ';
FOR x IN 1 .. v_keep.COUNT LOOP
v_partition1 := v_partition1 || v_keep(x) || ',';
END LOOP;
FOR y IN 1 .. v_pivot.COUNT LOOP
v_partition2 := v_partition2 || v_pivot(y) || ',';
END LOOP;
v_partition1 := rtrim(v_partition1, ',');
v_partition2 := rtrim(v_partition2, ',');
v_partition  := v_partition || v_partition1 || ',' || v_partition2 ||
', row_number() over (partition by ' || v_partition1 ||
' order by ' || v_partition2 || ') rn from ' || p_table;
v_partition  := rtrim(v_partition, ',');
v_sql        := 'select ';
FOR i IN 1 .. v_keep.COUNT LOOP
v_sql := v_sql || v_keep(i) || ',';
END LOOP;
FOR i IN 1 .. v_max_cols LOOP
FOR j IN 1 .. v_pivot.COUNT LOOP
v_sql := v_sql || ' max(decode(rn,' || i || ',' || v_pivot(j) ||
',null))' || v_pivot(j) || '_' || i || ',';
END LOOP;
END LOOP;
IF p_where IS NOT NULL THEN
v_sql := rtrim(v_sql, ',') || ' from (' || v_partition || ' ' ||
p_where || ') group by ';
ELSE
v_sql := rtrim(v_sql, ',') || ' from (' || v_partition ||
') group by ';
END IF;
FOR i IN 1 .. v_keep.COUNT LOOP
v_sql := v_sql || v_keep(i) || ',';
END LOOP;
v_sql := rtrim(v_sql, ',');
p_print_sql(v_sql);
OPEN p_refc FOR v_sql;
EXCEPTION
WHEN OTHERS THEN
OPEN p_refc FOR
SELECT 'x' FROM dual WHERE 0 = 1;
END;

PROCEDURE p_rows_column_real(p_table     IN VARCHAR2,
p_keep_cols IN VARCHAR2,
p_pivot_col IN VARCHAR2,
p_pivot_val IN VARCHAR2,
p_where     IN VARCHAR2 DEFAULT NULL,
p_refc      IN OUT refc) IS
v_sql VARCHAR2(4000);
TYPE v_keep_ind_by IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
v_keep v_keep_ind_by;
TYPE v_pivot_ind_by IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
v_pivot    v_pivot_ind_by;
v_keep_cnt INT;
v_group_by VARCHAR2(2000);
BEGIN
v_keep_cnt := length(p_keep_cols) - length(REPLACE(p_keep_cols, ',')) + 1;
FOR i IN 1 .. v_keep_cnt LOOP
v_keep(i) := f_split_str(p_keep_cols, ',', i);
END LOOP;
v_sql := 'select ' || 'cast(' || p_pivot_col ||
' as varchar2(200)) as ' || p_pivot_col || ' from ' || p_table ||
' group by ' || p_pivot_col;
EXECUTE IMMEDIATE v_sql BULK COLLECT
INTO v_pivot;
FOR i IN 1 .. v_keep.COUNT LOOP
v_group_by := v_group_by || v_keep(i) || ',';
END LOOP;
v_group_by := rtrim(v_group_by, ',');
v_sql      := 'select ' || v_group_by || ',';

FOR x IN 1 .. v_pivot.COUNT LOOP
v_sql := v_sql || ' max(decode(' || p_pivot_col || ',' || chr(39) ||
v_pivot(x) || chr(39) || ',' || p_pivot_val ||
',null)) as "' || v_pivot(x) || '",';
END LOOP;
v_sql := rtrim(v_sql, ',');
IF p_where IS NOT NULL THEN
v_sql := v_sql || ' from ' || p_table || p_where || ' group by ' ||
v_group_by;
ELSE
v_sql := v_sql || ' from ' || p_table || ' group by ' || v_group_by;
END IF;
p_print_sql(v_sql);
OPEN p_refc FOR v_sql;
EXCEPTION
WHEN OTHERS THEN
OPEN p_refc FOR
SELECT 'x' FROM dual WHERE 0 = 1;
END;

END;
/


3、多列转换成字符串
sql代码

CREATE TABLE t_col_str AS SELECT * FROM t_col_row;

SELECT * FROM t_col_str;


用||或concat函数可以实现

SELECT * FROM t_col_str;

SELECT ID, c1||','||c2||','||c3 AS c123 FROM t_col_str;


4、多行转换成字符串
未多行转换成字符串的效果:



多行转换成字符串的效果:



CREATE TABLE t_row_str(
ID INT,
col VARCHAR2(10));

INSERT INTO t_row_str VALUES(1,'a');
INSERT INTO t_row_str VALUES(1,'b');
INSERT INTO t_row_str VALUES(1,'c');
INSERT INTO t_row_str VALUES(2,'a');
INSERT INTO t_row_str VALUES(2,'d');
INSERT INTO t_row_str VALUES(2,'e');
INSERT INTO t_row_str VALUES(3,'c');
COMMIT;

SELECT * FROM t_row_str;


1)MAX + decode–>适用范围:8i,9i,10g及以后版本

SELECT id,
MAX(decode(rn, 1, col, NULL)) ||
MAX(decode(rn, 2, ',' || col, NULL)) ||
MAX(decode(rn, 3, ',' || col, NULL)) str
FROM (SELECT id,
col,
row_number() over(PARTITION BY id ORDER BY col) AS rn
FROM t_row_str) t
GROUP BY id
ORDER BY 1;


2)row_number + lead–>适用范围:8i,9i,10g及以后版本

SELECT id, str
FROM (SELECT id,
row_number() over(PARTITION BY id ORDER BY col) AS rn,
col || lead(',' || col, 1) over(PARTITION BY id ORDER BY col) ||
lead(',' || col, 2) over(PARTITION BY id ORDER BY col) ||
lead(',' || col, 3) over(PARTITION BY id ORDER BY col) AS str
FROM t_row_str)
WHERE rn = 1
ORDER BY 1;


3)MODEL–>适用范围:10g及以后版本

SELECT id, substr(str, 2) str FROM t_row_str
MODEL
RETURN UPDATED ROWS
PARTITION BY(ID)
DIMENSION BY(row_number() over(PARTITION BY ID ORDER BY col) AS rn)
MEASURES (CAST(col AS VARCHAR2(20)) AS str)
RULES UPSERT
ITERATE(3) UNTIL( presentv(str[iteration_number+2],1,0)=0)
(str[0] = str[0] || ',' || str[iteration_number+1])
ORDER BY 1;


4)sys_connect_by_path–>适用范围:8i,9i,10g及以后版本

SELECT t.id id, MAX(substr(sys_connect_by_path(t.col, ','), 2)) str
FROM (SELECT id, col, row_number() over(PARTITION BY id ORDER BY col) rn
FROM t_row_str) t
START WITH rn = 1
CONNECT BY rn = PRIOR rn + 1
AND id = PRIOR id
GROUP BY t.id;


5)wmsys.wm_concat–>适用范围:10g及以后版本

这个函数预定义按','分隔字符串,若要用其他符号分隔可以用,replace将','替换。

SELECT id, REPLACE(wmsys.wm_concat(col), ',', '/')
FROM t_row_str
GROUP BY id;


5、字符串转换成多列

其实际上就是一个字符串拆分的问题。

CREATE TABLE t_str_col AS
SELECT ID, c1||','||c2||','||c3 AS c123
FROM t_col_str;

SELECT * FROM t_str_col;


1)substr + instr–>适用范围:8i,9i,10g及以后版本

SELECT id,
c123,
substr(c123, 1, instr(c123 || ',', ',', 1, 1) - 1) c1,
substr(c123,
instr(c123 || ',', ',', 1, 1) + 1,
instr(c123 || ',', ',', 1, 2) - instr(c123 || ',', ',', 1, 1) - 1) c2,
substr(c123,
instr(c123 || ',', ',', 1, 2) + 1,
instr(c123 || ',', ',', 1, 3) - instr(c123 || ',', ',', 1, 2) - 1) c3
FROM t_str_col
ORDER BY 1;


2)regexp_substr–>正则表达式

适用范围:10g及以后版本

SELECT id,
c123,
rtrim(regexp_substr(c123 || ',', '.*?' || ',', 1, 1), ',') AS c1,
rtrim(regexp_substr(c123 || ',', '.*?' || ',', 1, 2), ',') AS c2,
rtrim(regexp_substr(c123 || ',', '.*?' || ',', 1, 3), ',') AS c3
FROM t_str_col
ORDER BY 1;


6.字符串转换成多行

CREATE TABLE t_str_row AS
SELECT id, MAX(decode(rn, 1, col, NULL)) || MAX(decode(rn, 2, ',' || col, NULL)) || MAX(decode(rn, 3, ',' || col, NULL)) str FROM (SELECT id, col, row_number() over(PARTITION BY id ORDER BY col) AS rn FROM t_row_str) t GROUP BY id ORDER BY 1;

SELECT * FROM t_str_row;


1)UNION ALL

适用范围:8i,9i,10g及以后版本

SELECT id, 1 AS p, substr(str, 1, instr(str || ',', ',', 1, 1) - 1) AS cv
FROM t_str_row
UNION ALL
SELECT id,
2 AS p,
substr(str,
instr(str || ',', ',', 1, 1) + 1,
instr(str || ',', ',', 1, 2) - instr(str || ',', ',', 1, 1) - 1) AS cv
FROM t_str_row
UNION ALL
SELECT id,
3 AS p,
substr(str,
instr(str || ',', ',', 1, 1) + 1,
instr(str || ',', ',', 1, 2) - instr(str || ',', ',', 1, 1) - 1) AS cv
FROM t_str_row
ORDER BY 1, 2;


适用范围:10g及以后版本

SELECT id, 1 AS p, rtrim(regexp_substr(str||',', '.*?' || ',', 1, 1), ',') AS cv
FROM t_str_row
UNION ALL
SELECT id, 2 AS p, rtrim(regexp_substr(str||',', '.*?' || ',', 1, 2), ',') AS cv
FROM t_str_row
UNION ALL
SELECT id, 3 AS p, rtrim(regexp_substr(str||',', '.*?' || ',',1,3), ',') AS cv
FROM t_str_row
ORDER BY 1, 2;


适用范围:10g及以后版本

SELECT t.id,
c.lv AS p,
rtrim(regexp_substr(t.str || ',', '.*?' || ',', 1, c.lv), ',') AS cv
FROM (SELECT id,
str,
length(regexp_replace(str || ',', '[^' || ',' || ']', NULL)) AS cnt
FROM t_str_row) t
INNER JOIN (SELECT LEVEL lv FROM dual CONNECT BY LEVEL <= 5) c ON c.lv <=  t.cnt
ORDER BY 1, 2;


4)Hierarchical + DBMS_RANDOM

适用范围:10g及以后版本

SELECT id,
LEVEL AS p,
rtrim(regexp_substr(str || ',', '.*?' || ',', 1, LEVEL), ',') AS cv
FROM t_str_row
CONNECT BY id = PRIOR id
AND PRIOR dbms_random.VALUE IS NOT NULL
AND LEVEL <=
length(regexp_replace(str || ',', '[^' || ',' || ']', NULL))
ORDER BY 1, 2;


5)Hierarchical + CONNECT_BY_ROOT

适用范围:10g及以后版本

SELECT id,
LEVEL AS p,
rtrim(regexp_substr(str || ',', '.*?' || ',', 1, LEVEL), ',') AS cv
FROM t_str_row
CONNECT BY id = connect_by_root id
AND LEVEL <=
length(regexp_replace(str || ',', '[^' || ',' || ']', NULL))
ORDER BY 1, 2;


6)MODEL

适用范围:10g及以后版本

SELECT id, p, cv FROM t_str_row
MODEL
RETURN UPDATED ROWS
PARTITION BY(ID)
DIMENSION BY( 0 AS p)
MEASURES( str||',' AS cv)
RULES UPSERT
(cv
[ FOR p
FROM 1 TO length(regexp_replace(cv[0],'[^'||','||']',null))
INCREMENT 1
] = rtrim(regexp_substr( cv[0],'.*?'||',',1,cv(p)),','))
ORDER BY 1,2;



oracle timestamp转换date及date类型相减

--timestamp转换为date(ts字段为timestamp类型)
SELECT cast(ts AS DATE) from tab1 WHERE tid=1;
--timestamp转换为date(ts字段为timestamp类型)
SELECT cast(ts AS DATE) from tab1 WHERE tid=3;
--date相减
SELECT (SELECT cast(ts AS DATE) from tab1 WHERE tid=3)-(SELECT cast(ts AS DATE) from tab1 WHERE tid=1) FROM dual;
--把date转换为妙
SELECT ((SELECT cast(ts AS DATE) from tab1 WHERE tid=3)-(SELECT cast(ts AS DATE) from tab1 WHERE tid=1))*24*60*60 FROM dual;



数据库的导入导出

一、导出:

打开CMD,输入命令: exp

Username: test@orcl

Password:

Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 – 64bit Production

With the Partitioning, Real Application Clusters and Data Mining options

Enter array fetch buffer size: 4096 > //设置缓存大小,默认4096,无需修改

Export file: expdat.dmp > D:\myproject_20120927.dmp //存放导出的文件名

(2)U(sers), or (3)T(ables): (2)U > 2 //选择导出的范围,

//2是导出该用户下所有数据,包括表、存储过程、sequence等;

//3是只导出某个表;通常备份选择2。

Export table data (yes ): yes > //是否导出表中的数据
二、导入:

打开CMD,输入命令: imp fromuser=test touser=test ignore=y file=D:\myproject_20120927.dmp

fromuser是指你导出的库的用户ID;

touser是指你导入的库的用户ID;

ignore是指是否忽略已经存在的表;

file是指等待导入的文件。

比如你从test@orcl导出了一个文件放在D:\myproject_20120927.dmp,现在你要导入本机other_test@other_orcl,那你的脚本就是imp
fromuser=test touser=other_test file=D:\myproject_20120927.dmp

然后会提示你输入用户名密码,然后就开始导数据了。注意命令里面的等号前后不要有空格。


将Oracle数据库设置为归档模式及非归档模式

一、将Oracle数据库设置为归档模式

1)sql>shutdown normal/immediate;

2)sql>startup mount;

3)sql>alter database archivelog;

4)sql>alter database open;

5)archive log list;
注意:show parameter log_archive_dest查看归档日志的存放位置。
二、将Oracle数据库设置为非归档模式

1)、关闭数据库

shutdown immediate

2)、再后面把数据库启动到mount的模式

startup mount

3)、关闭flash闪回数据库模式,如果不关闭的话,在后面关闭归档日志的时候就会出现讨厌的ora-38774错误。

alter database flashback off

4)、接着把数据库改为非归档模式

alter database noarchivelog;

5)、都修改好了以后,然后打开数据库

alter database open;

6)、察看一下归档日志的空间大小

select * from v$recovery_file_dest;

接着看一下log日志的状态

select * from v$log;

再看一下闪回日志使用状况

select * from v$flash_recovery_area_usage;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: