楼主: dingjun123

[精华] 【PDF已传上】准备写一个oracle高级sql的参考资料

[复制链接]
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
11#
 楼主| 发表于 2008-10-21 20:10 | 只看该作者
原帖由 adminsys 于 2008-10-21 18:58 发表
顶你,能加关于cursor,触发器,存储过程这三大块的内容及应用么


这个在PL/SQL开发指南上有了

使用道具 举报

回复
论坛徽章:
13
铁扇公主
日期:2008-08-23 12:15:03奥运会纪念徽章:足球
日期:2008-08-28 20:19:082009日食纪念
日期:2009-07-22 09:30:002010年世界杯参赛球队:葡萄牙
日期:2010-03-11 08:38:072010年世界杯参赛球队:加纳
日期:2010-03-17 14:44:352012新春纪念徽章
日期:2012-01-04 11:53:29奥运会纪念徽章:拳击
日期:2012-06-18 16:33:22蛋疼蛋
日期:2013-04-03 16:41:24
12#
发表于 2008-10-21 20:11 | 只看该作者
支持一下,我也想整理一下这些东西,正好参考你的,呵呵,名字都想好了,就叫 sql的优雅实现
我觉得不一定要限制在高级查询,只要是好的实现方式都可以
补充一点,正则表达式

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
13#
 楼主| 发表于 2008-10-21 20:17 | 只看该作者
原帖由 homeworld80 于 2008-10-21 20:11 发表
支持一下,我也想整理一下这些东西,正好参考你的,呵呵,名字都想好了,就叫 sql的优雅实现
我觉得不一定要限制在高级查询,只要是好的实现方式都可以
补充一点,正则表达式


ok,关键是这些查询中的大部分在一般需求中用的很少,所以就叫高级查询了,oracle也是这么叫的,看到论坛中的很多人问的问题主要是体现在这些方面,所以就想总结一下,也让自己学习一把,嘿嘿

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
14#
 楼主| 发表于 2008-10-21 20:19 | 只看该作者
[php]
8.rownum—top-N分析
Rownum是oracle中的一个伪列,其目的就是给查询的行标注行号。可以实现查询前n行,中间几行,最后几行的功能(根据业务功能定义行的排序)。但是rownum又是一个很特殊的列,使用过程中,首先要弄清楚oracle的原理,然后加以分析。
注意点:
1.rownum不可以直接在前面加上表名或别名等。
如 select t.rownum from table t;是错误的。

2.rownum和where在同一层查询中,where条件之后使用rownum比较,只能使用<=,<不能使用>,>=,=(使用=,只能是where rownum=1才可以)。否则返回null。如果使用!=或<>,那么只是返回前n-1行,其他按照rownum工作原理推算。
如 select rownum,name from emp where rownum>=5;没有结果
   Select rownum,name from emp where rownum=1;返回第1行
   Select rownum,name from emp where rownum !=10;//返回1-9行

3.当rownum和排序在一个语句中使用的时候,要注意,看看oracle有没有使用索引,如果使用了索引扫描,那么可能按照索引来组织数据,如果没有则是先生成行号,然后order by,则查询出来的结果乱序。可以采用嵌套查询,先在内层排序,在外层查询rownum。

Rownum原理:
1 Oracle executes your query.
1.执行查询操作
2 Oracle fetches the first row and calls it row number 1.
2.将第一行的row num置为1
3 Have we gotten past row number meets the criteria? If no, then Oracle discards the row, If yes, then Oracle return the row.
3.将得到的行的row num与条件相比较,如果不匹配,则抛弃行,如果匹配,则返回行
4 Oracle fetches the next row and advances the row number (to 2, and then to 3, and then to 4, and so forth).
4.oracle获取下一行,然后将rownum增1
5 Go to step 3.
5.返回第3步

从这个原理可以知道,select rownum,name from emp where rownum>5;不返回行,因为首先执行查询select name from emp,将第1行的rownum标为1,然后看where条件,为false,则抛弃行,执行第2行,还是rownum标为1,看where条件还为false,所以永远是false,rownum不改变,所有的行都被抛弃,所以没有结果。
基于以上的问题,那么使用rownum的常用查询结构有:
3.        简单的加上行号
选择员工,显示姓名,结果集的行号               
select rownum ,last_name from s_emp;
select rownum ,last_name from s_emp where rownum <= 10;
select rownum ,last_name from s_emp where rownum = 1;

下面的不正确
select rownum ,last_name from s_emp where rownum = 10;
select rownum ,last_name from s_emp where rownum >= 10;

4.        获取前n行
选择员工,显示姓名,行号,按照姓名排序       
select rownum,last_name from s_emp order by last_name;--不正确
如果要排序,那么需要在内层查询中排序好,然后外层查询引入rownum(两重嵌套查询)
select rownum,last_name from
      (select last_name from s_emp order by last_name);
选择前10行,可以直接在两重嵌套的外层查询中引入rownum,并且where rownum<=
select rownum,last_name from
      (select last_name from s_emp order by last_name)
      where rownum<=10;

3.获取中间行数据
选择员工姓名,显示行号,按照姓名排序,显示第5条记录到第10条记录       
select rn,last_name from --最外层用where条件判断
     (select rownum rn,last_name   --中间层查询加上行号
     from (
        select last_name from s_emp order by last_name   --底层查询排序
        )
     )
   where rn between 5 and 10;

也可以在中间层查询中加入最大的行号判断,<=,最外层用>=,上面的查询可以用下面的替换:
select rn,last_name from
(select rownum rn,last_name
   from (
    select last_name from s_emp order by last_name
   )
   where rownum<=10  --中间层查询把要查询的最大行号最过滤
)
where rn>=5;
4.取最大值的行
查找出薪资差距最大的部门,显示名称

select name
  from s_dept
where id = (select dept_id
               from (select dept_id, (max(salary) - min(salary)) salary  --内层查询将薪水差计算作为别名,排序
                       from s_emp
                      group by dept_id
                      order by salary desc)
              where rownum = 1); --外层查询取得第一行数据
其他的最小,后前行等都可以通过修改排序获得。

[/php]

[ 本帖最后由 dingjun123 于 2008-11-15 12:30 编辑 ]

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
15#
 楼主| 发表于 2008-10-21 20:22 | 只看该作者
[php]
7.case表达式
7.1 case简介
        Case表达式,是可以在sql中使用if ..then..else的逻辑判断,而避免使用PL/SQL的有效方法,在sql中主要有两种:简单case和搜索case,在plsql中还有两种种case语句。与decode类似。(有一定的区别,见下面第四条)

   1.Case表达式返回的是一个确定的value,如果没有else,若前面的都不匹配,则返回null。<else不是必须的,都没有匹配返回null,这与pl/sql中的case语句不同,case语句如果不写else,都没有匹配,则报case_not_found异常>

   2.简单case中的表达式,when后面的表达式类型应该全部保持一致。如:
select case 'a' when 'a' then 1 when 9 then 3 end from dual;--所有的when类型必须与第case之后的表达式值类型保持一致,资料的9应该是’9’,没有自动转换成char,和一般的sql中自动转换不同。

3.所有的then后面的return_value类型要保持一致
select case 'a' when 'a' then '1' when '9' then '3' else 3 end from dual;--红色部分类型应该保持一致,没有自动转换

4.对于简单case表达式,也就是case 表达式 when…那么when null总是取不到。也就是case后面的表达式如果值为null,不会与when null匹配,只会与else匹配。如:
select case null when null then 'null' else 'not matched!' end from dual;--case的null不会与when后面的null匹配,只会返回else的结果。
   关于这点,如果case后面的表达式有可能为null,如果需要匹配null,那么可以使用decode和searched case。
   Decode:
decode(exp,
value1,res1,
value2,res2,….,
valuen resn,
elsevalue)。
如果其中有存在exp为null,那么如果valuei中有null,则会匹配,返回resi。如:
select decode(null,'a1','1','a2','2',null,'null','not know!') from dual;--返回字符串null

   searched case:
case when
condition_1 then value1
when condition_2 then value2…
when condtion_i then valuei
else
elsevalue
如果要匹配null,只需要 case when exp is null then ..就可以了

5.对于searched case来说,有自动类型转换,只要条件成立就可以。如:
select case when 1='1' then 1 end from dual;--1=’1’条件成立

6.参数最高限制255个。包括case exp中的exp和else中的,以及when exp1 value 1算两个参数。如果语句复杂,超过这个限制,可以考虑使用嵌套case。
7.2 两种case
简单的case:
    语法:case exp when comexp then returnvalue
                 ..when comexp then returnvalue
                 Else
                 Returnvalue
                 End
使用规则case简介。
Case到end之间相当于一个具体的值,可以做运算,取别名,嵌套case等等。只要把case到end当作一个运算结果的表达式就可以了。

《注意,中间一直到end都没有其他标点符号》


搜索case:
        直接使用case when +比较条件 到end为返回的结果,end之后可以做运算。
        语法:case   when Boolean then returnvalue
                 ..when Boolean then return value
                 Else
                 Returnvalue
                 End
简单case例子
SELECT cust_last_name,
CASE credit_limit WHEN 100 THEN ’Low’
WHEN 5000 THEN ’High’
ELSE ’Medium’ END
FROM customers;

搜索case例子
select case when id between 1 and 10 then 'low'
            when id between 20 and 30 then 'mid'
            when id between 40 and 50 then 'high'
            else 'unknow'
            end
       from product;
--加工资 salary<2000的加10%,2000<=salary<=4000的加5%,4000以上的加4%,刚入职的没有工资记录的不做
update emp set
  salary=
  case when salary<2000 then salary*1.1
       when salary between 2000 and 4000 then salary*1.05
       when salary>4000 then salary*1.04
       else
       null
       end;           --可以在case …end中加括号区分。

select case when name like '全球%' then 'true'
       when name like '神州行' then 'false'
       else 'mm'
       end
       from trademark;
比较操作,可以使用like,between … and ..,!=,<,>=等操作符以及其他返回boolean类型的操作符。


简单case和searched case之间的区别:
1.        简单case只能是when后面的表达式完全匹配case后的表达式,相当于 =,所以也不能匹配null。
2.        searched case可以作为比较条件,那么可以使用like,!=,between ..and,<,=,is null,is not null等,比简单case的使用更加广泛,完全可以替代简单
[/php]

[ 本帖最后由 dingjun123 于 2008-11-15 12:32 编辑 ]

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
16#
 楼主| 发表于 2008-10-21 20:29 | 只看该作者
[php]
5.层次查询(Hierarical Retrival)
5.1 基本概念
对于层次查询需要掌握:
1.解释层次查询的基本概念,识别需求中需要用到层次查询的能力。
2.建立和格式化一个树形报表(tree report)。
3.修剪树形结构的节点(node)和枝(branches)。
关键词:tree,root(根),node,leaf(叶子),branch(树枝,分支)
本节例子来源于表s_emp,表结构和数据如下:

看上面的表s_emp,使用层次查询,我们可以获得一张表基于层次关系的数据集合。Oracle是一
种关系型数据库,在表中不可能以层次的关系存放数据。但是我们可以通过一定的规则,使用tree walking(树的遍历或树的查找)来获得层次关系的数据。Hierarical query是一种获得树的层析关系报表的方法。
   树形结构的数据集合,存在于我们日常生活中的很多地方,比如考虑一个家族关系,有长辈,长辈下面有子女,子女下面还可以有子女,这转化为层次或等级关系就是:根节点只有一个,下面有子节点,子节点下面还有子节点,这样组成了一棵树。(有时候,根节点root不一定只有一个,严格意义上说,这种情况不是一个严格的树)。
  当一种层次关系是存在一个表的行中,那么这种层次是可以获得的。例如,我们看s_emp表,对于title:VP,我们知道这些行中都包含manager_id=1,也就是说,这些行属于id=1的雇员的下属雇员,那么有title=vp又可以获得一系列的层次,而这些层次的跟则是id=1这个雇员。由此,得到一棵树形结构数据集合。
    层次树或等级树,在譬如家族关系,育种关系,组织管理,产品装配,人类进化,科学研究等领
广泛应用。
    下面我们就根据s_emp这张表,根据职位大小来描述一个树形结构图。如图:
树形结构的父子关系,你可以控制:
1.        遍历树的方向,是自上而下,还是自下而上。
2.        确定层次的开始点(root)的位置。
层次查询语句正是从这两个方面来确定的,start with确定开始点,connect by确定遍历的方向。
   
5.2 层次查询

5.2.1 语法:
select [level],column,exper,...
  from table
[where condition]
[start with conditions]
[connect by conditions]
层次查询是通过start with和connect by子句标识的。
1.其中level关键字是可选的,表示等级,表示root,2表示root的child,其他相同的规则。
2.From之后可以是table,view但是只能是一个table。
3.Where条件限制了查询返回的行,但是不影响层次关系,属于将节点截断,但是这个被截断的节点的下层child不受影响。
4.Start with是表示开始节点,对于一个真实的层次关系,必须要有这个子句,但是不是必须
的,后面详细介绍。
5.connect by prior是指定父子关系,其中prior的位置不一定要在connect by之后,
对于一个真实的层次关系,这也是必须的。
对于from是视图的,那么这个view不能包含join。
5.2.2 遍历树
    Start with子句:
            首先必须确定startpoint,通过start with子句,后面加条件,这个条件是任何合法的条件表达式。
    Start with确定将哪行作为root,如果没有start with,则每行都当作root,然后查找其后代,这不是一个真实的查询。Start with后面可以使用子查询,如果有where条件,则会截断层次中的相关满足条件的节点,但是不影响整个层次结构。可以带多个条件。
   对于s_emp,从root title=president开始,语句如下:
select level,id,manager_id,last_name,title from s_emp
      start with manager_id is null
      connect by prior id=manager_id;
这个语句与下面的语句等价,将start with 后面的条件换成子查询。
select level,id,manager_id,last_name,title from s_emp
      start with title=(select title from s_emp where manager_id is null)
      connect by prior id=manager_id;
注意:start with 和connect by prior不是ANSI标准sql。

   Connect by子句:
   Connect by与prior确定一个层次查询的条件和遍历的方向(prior确定)。
Connect by prior column_1=column_2;(其中prior表示前一个节点的意思,可以在connect by等号的前后,列之前,也可以放到select中的列之前)。
  Connect by也可以带多个条件,比如 connect by prior id=manager_id and id>10;
   方向:
1.        自顶向下遍历:就是先由根节点,然后遍历子节点。column_1表示父key,column_2表示子key。即这种情况下:connect by prior 父key=子key表示自顶向下,等同于connect by 子key=prior 父key.
例如:
select level,id,manager_id,last_name, title from s_emp
      start with  manager_id=2
      connect by id=prior manager_id;--自下而上遍历

2.        自底向上遍历:就是先由最底层的子节点,遍历一直找到根节点。与上面的相反。
Connect by之后不能有子查询,但是可以加其他条件,比如加上and id !=2等。这句话则会截断树枝,如果id=2的这个节点下面有很多子孙后代,则全部截断不显示。比如下面的句子:
select level,id,manager_id,last_name,title from s_emp
      start with title=(select title from s_emp where manager_id is null)
      connect by prior id=manager_id and id!=2;
不来不加上id!=2,共有25条记录,现在加上这个条件只有9条记录了,因为id=2的后代包括自己共有16条记录,全部被截断。




5.2.3 使用level和lpad格式化报表
   Level是层次查询的一个伪列,如果有level,必须有connect by,start with可以没有。Lpad是在一个string的左边添加一定长度的字符,并且满足中间的参数长度要求,不满足自动添加。例如现在的需求是,输出s_emp等级报表,root节点的last_name不变,比如第2等级,也就是level=2的前面加两个’_’符号,level=3的前面加4个。这样我们可以得到一个公式就是:
   Lpad(last_name,length(last_name)+(level*2)-1,’_’)
可以得出下面的语句:
select level,id,manager_id,lpad(last_name,length(last_name)+(level*2)-2,'_'),title,prior last_name from s_emp
      start with manager_id is null
      connect by prior id=manager_id;
select中的prior last_name是输出其父亲的last_name.这个语句执行的结果如下:
5.2.4 修剪branches
  上面已经提到,where子句会将节点删除,但是其后代不会受到影响,connect by 中加上条件会将满足条件的整个树枝包括后代都删除。要注意,如果是connect by之后加条件正好条件选到根,那么结果和没有加一样,如图所示:


5.2.5 层次查询限制
1.层次查询from 之后如果是table,只能是一个table,不能有join。
2.from之后如果是view,则view不能是带join的。
3.使用order by子句,order子句是在等级层次做完之后开始的,所以对于层次查询来说没有什么意义,除非特别关注level,获得某行在层次中的深度,但是这两种都会破坏层次。见5.3增强特性中的使用siblings排序。
4.在start with中表达式可以有子查询,但是connect by中不能有子查询。


5.2.6 应用
1)查询每个等级上节点的数目
先查看总共有几个等级:
select count(distinct level)
from s_emp
start with manager_id is null
connect by prior id=manager_id;
要查看每个等级上有多少个节点,只要按等级分组,并统计节点的数目即可,可以这样写:
select level,count(last_name)
from s_emp
start with manager_id is null
connect by prior id=manager_id
group by level;

2)格式化报表
见5.2.3.

3)查看等级关系
    有一个常见的需求,比如给定一个具体的emp看是否对某个emp有管理权,也就是从给定的节点寻找,看其子树节点中能否找到这个节点。如果找到,返回,找不到,no rows returned.
    比如对于s_emp表,从根节点,也就是manager_id is null的开始找,看first_name=’ Elena’是否被它管理,语句如下:
select level,a.* from
s_emp a
where first_name='Elena' –被管理的节点
start with manager_id is null –开始节点
connect by prior id=manager_id;

4)删除子树
                比如有这样的需求,现在要裁员,将某个部门的员工包括经理全部裁掉,那么可以使用树形查询作为子查询实现这个功能。
    将id为2的员工管理的所有员工包括自己删除。因为要全部裁掉了。那么语句如下:
delete from s_emp where id in(
select id from
s_emp a
start with id=2 –从id=2的员工开始查找其子节点,把整棵树删除
connect by prior id=manager_id);

5)找出每个部门的经理
   这个需求,我们可以从树中查找,也就是对于每个部门选最高等级节点。可以使用connect by后加条件过滤branches的方法。
   select level,a.* from
s_emp a
start with manager_id is null
connect by prior id=manager_id and dept_id !=prior dept_id;--当前行的dept_id不等于前一行的dept_id,即每个子树中选最高等级节点

6)查询一个组织中最高的几个等级
   用where level条件过滤
select level,a.* from
s_emp a
  where level <=2 –查找前两个等级
start with manager_id is null
connect by prior id=manager_id and dept_id !=prior dept_id;

7)合计层次
  有两个需求,一是对一个指定的子树subtree做累加计算salary,一是将每行都作为root节点,然后对属于这个节点的所有字节点累加计算salary。
第一种很简单,求下sum就可以了,语句:
select sum(salary) from
s_emp a
start with id=2—比如从id=2开始
connect by prior id=manager_id;

第2个需求,需要用到第1个,对每个root节点求这个树的累加值,然后内部层次查询的开始节点从外层查询获得。
select last_name,salary,(
  select sum(salary) from
s_emp
start with id=a.id –让每个节点都成为root
connect by prior id=manager_id) sumsalary
from s_emp a;

8)找出指定层次中的叶子节点
  Leaf(叶子)就是没有子孙的孤立节点。Oracle 10g提供了一个简单的connect_by_isleaf=1,
0表示非叶子节点

select level,id,manager_id,last_name, title from s_emp
    where connect_by_isleaf=1 –表示查询叶子节点
      start with  manager_id=2
      connect by prior id=manager_id;
也可以通过连接查询获得,方法有多种,叶子节点肯定是level最大的节点。
select a.lev,b.id from (   
select max(level) lev from s_emp  --查询最大的level
      start with  manager_id=2
      connect by prior id=manager_id) a,
(select level lev,id
from s_emp
start with manager_id=2
connect by prior id=manager_id) b
      where a.lev=b.lev;
注意:level不可以前面加表名

其他:
Connect by 与rownum的联合使用,比如给定两个日期,查询中间所有的日期,按月递增:
SELECT to_date('2008-10-1', 'YYYY-MM-DD') + ROWNUM - 1
  FROM dual
CONNECT BY rownum <= to_date('2008-10-5', 'YYYY-MM-DD') -
           to_date('2008-10-1', 'YYYY-MM-DD') + 1







5.3 增强特性
   5.3.1 SYS_CONNECT_BY_PATH
Oracle 9i提供了sys_connect_by_path(column,char),其中column是字符型或能自动转换成字符型的列名。它的主要目的就是将父节点到当前节点的”path”按照指定的模式展现出现。这个函数只能使用在层次查询中。
例如,要求将s_emp表中的层次关系展现出来,并且将last_name按照’=>’展现。如root,则是=>root_last_name, level=2的就是=>root_last_name=>level_2_last_name,并且利用lpad格式化报表。语句是:
select last_name,
       level,
       id,
       lpad('   ', level * 2 - 1) || sys_connect_by_path(last_name, '=>') –前面按层次加空格,--并且后面加上路径
  from s_emp
start with manager_id is null
connect by prior id = manager_id;
下面的是oracle10g新增特性

5.3.2 CONNECT_BY_ISLEAF
    在oracle9i的时候,查找指定root下的叶子节点,是很复杂的,oracle10g引入了一个新的函数,connect_by_isleaf,如果行的值为0表示不是叶子节点,1表示是叶子节点。
  找出s_emp中找出manager_id=2开始的行为root,表示叶子节点和非叶子节点,那么语句如下:

select level,
       id,
       manager_id,
       last_name,
       title,
        (case --使用case表达式判断是否是叶子节点
         when connect_by_isleaf = 1 then
          '叶子'
         else
          '不是叶子'
       end) isleaf
  from s_emp
start with manager_id = 2
connect by prior id = manager_id;


5.3.3 CONNECT_BY_ISCYCLE和NOCYCLE关键字
    如果从root节点开始找其子孙,找到一行,结果发生和祖先互为子孙的情况,则发生循环,oracle会报ORA-01436: CONNECT BY loop in user data,在9i中只能将发生死循环的不加入到树中或删除,在10g中可以用nocycle关键字加在connect by之后,避免循环的参加查询操作。并且通过connect_by_iscycle得到哪个节点发生循环。0表示未发生循环,1表示发生了循环,如:
create table family1(
fatherid number,
childid number
);
insert into family1 values(null,1);
insert into family1  values(1,2);--父节点为1
insert into family1  values(1,3);
insert into family1  values(2,4);--发生循环
insert into family1  values(4,1);--子节点为1
insert into family1  values(4,5);
commit;

select connect_by_iscycle, fatherid,childid,sys_connect_by_path(childid,'/')
      from family1
      start with  fatherid is null
      connect by nocycle prior childid=fatherid;

5.3.4 CONNECT_BY_ROOT
    Oracle10g新增connect_by_root,用在列名之前表示此行的根节点的相同列名的值。如:
select connect_by_root last_name root_last_name, connect_by_root id root_id,
       id,last_name,manager_id
       from s_emp
       start with manager_id is null
       connect by prior id=manager_id;

5.3.5 使用SIBLINGS关键字排序
     前面说了,对于层次查询如果用order by排序,比如order by last_name则是先做完层次获得level,然后按last_name排序,这样破坏了层次,比如特别关注某行的深度,按level排序,也是会破坏层次的。
     在oracle10g中,增加了siblings关键字的排序。
语法:order  siblings  by <expre>
     它会保护层次,并且在每个等级中按expre排序。
select level,
       id,last_name,manager_id
       from s_emp
       start with manager_id is null
       connect by prior id=manager_id
       order siblings by last_name;

--------------------------------------------
16.扩展DDL和DML语句
16.1 背景和目标
  本章内容所用的实例表是第5章—层次查询所用到的s_emp表。结构如下:
Name           Type          Nullable Default Comments
-------------- ------------- -------- ------- --------
ID             NUMBER(7)                              
LAST_NAME      VARCHAR2(25)                           
FIRST_NAME     VARCHAR2(25)  Y                        
USERID         VARCHAR2(8)   Y                        
START_DATE     DATE          Y                        
COMMENTS       VARCHAR2(255) Y                        
MANAGER_ID     NUMBER(7)     Y                        
TITLE          VARCHAR2(25)  Y                        
DEPT_ID        NUMBER(7)     Y                        
SALARY         NUMBER(11,2)  Y                        
COMMISSION_PCT NUMBER(4,2)   Y      
本章目标:
1.能够描述多表insert的特性,并能够明确其使用场合。
2.掌握和使用不同类型的多表insert操作:
2-1.无条件的多表insert all
2-2.带条件的多表insert all
2-3.带条件的多表insert first
2-4.Pivoting insert
3.能够建立和使用external tables

关键字:conditional,unconditional,pivote,external,dictionary等。

背景描述:
   本章内容主要讨论,如何使用一条insert语句,向一个目标表插入多行记录,或者向多个目标表中插入多条记录,这些数据源可以来自于一个表或多个表,同过select语句获得数据源记录。常用于一些比较复杂的需求,如数据仓库的ETL中。多表insert在oracle9i中被加入。
16.2 经常使用的扩展
1.一般的insert操作。
   使用语法insert into table_name[(column[,column...])] values (value[,value…])的insert语句,每条insert只能插入到目标表中一条指定的数据。如果有很多行需要插入,而且这些数据来源于别的表或多个表之间提取的数据,那么这就不能满足要求了。因此oracle提供了一个扩展,见下面描述。

2.insert into table_name[(column[,column…])] select_statement
Oracle提供了一个扩展,将通过select语句查询的记录插入到指定表的所有列或者指定列中。每次只能插入到一个表中,但是性能已经比写很多条insert语句要高了。另外oracle还提供了直接create table table_name as select….(as不可以少!),实现直接创建表并且插入数据,依赖于后面的select语句的结果,比如只复制表结构,约束不赋值,那么可以使用
Create table new_table as select * from source_table where 1=0; --1=0不查询记录,只复制基本结构。
比如有个需求:表mxt新增了一个列time,那么这个新增的列在oracle中会被放在所有列的后面,现在想把这个time列放在第2个位置,那么可以使用这个操作:
create table copy_mxt as select id,time,name from mxt;--按指定顺序copy表到copy_mxt中
drop table mxt;  --删除源表mxt
rename copy_mxt to mxt;--将copy的表命名为目标表
下面具体说明oracle对insert操作的其他扩展,实现目标,一条insert操作,插入到多个表中,每个表插入多条记录。
16.3向多个目标insert
  这里有四种类型:
1.        无条件的insert
2.        带条件的insert all
3.        带条件的insert first
4.        pivoting insert
多表insert使用限制:
1.        只能对table使用多表insert,不能对视图或物化视图使用。
2.        不能对远程表进行这个插入操作。
3.        在做多表insert操作,不能指定一个表的集合表达式。
4.        多表insert中的的into目标表加在一起的列数不能超过999个。

语法:
  
注:如果没有conditional_insert_clause,必须有all,即insert all开头。如果有conditional_insert_clause,则all和first只能取其一。

16.3.1 无条件的insert操作
这里所说的无条件的insert操作有两种,一种就是单表insert into dest_table select..,一种是向多个目标中insert,那么使用insert all into dest_table1,dest table2…select…
目标表只有一个:insert into dest_table[colum(,column…)] select …
目标表有多个: insert all        --可以替代上面的操作
             Into dest_table1[column(,column…)] [values(…)]
             Into dest_table2[column(,column…)] [values(…)]
             …
             Select_statement—values中指定的是select结果中的列
   目标表只有一个的,那么插入目标表中后面可以跟指定的列,依赖于查询出来的数目以及类型,没有values指定。如果不指定列,那么表示目标表的列类型和顺序与select语句查询的结果完全一致。

   对于目标表有多个的必须使用insert all,all不能省略。与带条件的不同,带条件的all可以省略,后面讲解。这里的每个目标表可以不指定列,依赖于values中指定的select语句查询出来的结果中的列。如果目标表不指定列,那么表示目标表的列的顺序类型和values中指定的一致,而values中的列则依赖于select语句查询出的结果。当然也可以省略values,那么表示目标表中的列顺序和类型和select语句查询出来的结果完全一致,否则报错。Insert all可以替代目标表只有一个的操作。
建议不要省略目标表中的列和values,增强可读性。

例如:
create table emp_1 as select id,last_name from s_emp where 1=0;
create table emp_2 as select * from s_emp where 1=0;
create table emp_3 as select * from s_emp where 1=0;
--没有条件,向多个目标表全量插入,必须有all
insert all
--不指定emp_1后面的列,也不指定values,那么emp_1中的所有列类型和顺序与查询的列的类型和顺序一致
--也就是emp_1中只有查询结果中的那几列,而且类型和顺序与其一致
into emp_1
--指定了emp_2后面的列,没有values,表示emp_2中要插入的列被选择出来,与查询的结果列类型和顺序一致
--emp_2中也可能有很多列,不止这两列
into emp_2(id,last_name)
--指定emp_3后面的列,也指定values,那么values后面的列名必须与查询结果一致,如果
--查询中有别名,必须在values中使用别名。emp_3中指定的列类型和顺序必须与values保持一致
--emp_3中也可能列数大于指定的列数
into emp_3(id,last_name) values(s_id,s_last_name)
select id s_id,last_name s_last_name
  from s_emp;


这里总共插入了75行记录,select语句查询的记录为25行,因为是无条件的插入,那么对每个目标表都插入了25行记录,所以总共插入75行记录。

16.3.2 带条件的insert all
    带条件的insert all与不带条件的insert all相比可以省略all,但是不建议省略。另外的区别就是插入到目标表之前,有条件判断,从查询的结果行中筛选出满足条件的记录,然后向目标表中插入,而不在是无条件的insert all中的全量插入。
语法是:
      Insert all
      When 条件 then
      Into dest[指定列] values(查询中的列)
      When 条件 then
       …
      [else]
      Into dest[指定列] values(查询中的列)
      Select语句.
是否可以省略目标表中的指定列和values那么与无条件的insert all一致。条件中的列那么必须与查询的结果列名保持一致,如果select中使用别名,那么条件中也必须使用别名,和values中的规则一样。Else可以指定,如果不指定else,则按照条件筛选,如果指定else,则表示与前面全不匹配的插入到else后面的目标表中。带条件的insert all与insert first的区别在于只按条件对查询的结果进行筛选,不会不考虑前面已经匹配过的记录,每次都是全量匹配,而insert first是后面的when条件insert操作会对前面已经匹配过的记录不予考虑。
例如:

--conditional insert all
insert all
--将查询结果中为s_id>20的插入,条件中指定的列必须与查询的结果名字一致,如果有别名,用别名
when s_id>20 then
  into emp_1
--s_last_name为M开头的插入,可能插入的行与s_id>20有重复
when s_last_name like 'M%'then
  into emp_2(id,last_name)
--如果指定else,则不满足上面两个条件的插入到emp_3,插入的行不会与上面两个重复
else
  into emp_3(id,last_name) values(s_id,s_last_name)
select id s_id,last_name s_last_name
  from s_emp;

总共插入26行,而查询结果为25行,emp_2中插入了一条与emp_1中重复的记录。

16.3.3 带条件的insert first
Insert first只有带条件的,没有不带条件的。
语法只要将insert all中的all改为first就可以了。这里的first不可以省略。省略那么默认就是all。
它的插入规则是如果前面的when匹配过的,下一个when会自动不考虑上面已经匹配过的行记录。比如将上面16.3.2中的例子改为:
insert first
--将查询结果中为s_id>20的插入,条件中指定的列必须与查询的结果名字一致,如果有别名,用别名
when s_id>20 then
  into emp_1
--s_last_name为M开头的插入,可能插入的行与s_id>20有重复
when s_last_name like 'M%'then
  into emp_2(id,last_name)
--如果指定else,则不满足上面两个条件的插入到emp_3,插入的行不会与上面两个重复
else
  into emp_3(id,last_name) values(s_id,s_last_name)
select id s_id,last_name s_last_name
  from s_emp;

    插入25行,因为when s_last_name like 'M%'不考虑前面已经匹配的行,所以emp_2中就没有那条与插入emp_1中的重复行。

16.3.4 pivoting insert
这节主要讨论使用pivoting insert实现将非关系性表记录转换为关系型表中存储。
比如一个表为sales_source_data,里面的列为
   employee_id number,week_id number,sales_mon number,sales_tue number,sales_wed number,sales_thur number,sales_fri number
   员工id,周id,其他的是工作日的每天的销售记录。
现在要把数据转到sales_info表中,这个表的列为employee_id,week_id,sales。
   
   要实现这个转换可以使用pivoting insert操作。和insert all类似,只不过这里的多表都是同一个表。
   
insert all
  into sales_info values(employee_id,week_id,sales_mon)  --分别按每个工作日插入
  into sales_info values(employee_id,week_id,sales_tue)
  into sales_info values(employee_id,week_id,sales_wed)
  into sales_info values(employee_id,week_id,sales_thur)
  into sales_info values(employee_id,week_id,sales_fri)
select * from sales_source_data;

比如原来的sales_source_data中只有一条记录,那么插入到sales_info中就变成了5条记录。
select * from sales_source_data;


[/php]

[ 本帖最后由 dingjun123 于 2008-11-17 10:20 编辑 ]

tablescripts.sql

42.23 KB, 下载次数: 355

使用道具 举报

回复
论坛徽章:
24
授权会员
日期:2008-07-25 12:30:27咸鸭蛋
日期:2012-03-30 17:00:27灰彻蛋
日期:2012-03-15 17:47:31ITPUB十周年纪念徽章
日期:2011-11-01 16:21:152010广州亚运会纪念徽章:篮球
日期:2011-03-23 17:59:312010广州亚运会纪念徽章:棒球
日期:2011-02-17 08:25:242010广州亚运会纪念徽章:棋类
日期:2011-01-04 08:32:45ITPUB9周年纪念徽章
日期:2010-10-08 09:31:212010新春纪念徽章
日期:2010-03-01 11:08:262010新春纪念徽章
日期:2010-01-04 08:33:08
17#
发表于 2008-10-21 21:15 | 只看该作者
这些内容,出一本1000页的书都不为过。

纯粹支持楼主!!!

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
18#
 楼主| 发表于 2008-10-21 21:27 | 只看该作者
原帖由 caizhuoyi 于 2008-10-21 21:15 发表
这些内容,出一本1000页的书都不为过。

纯粹支持楼主!!!


3Q,我准备写的简单点,让没有接触过的看一下基本会用就行了,具体的在什么场合使用,效率高点,sql写的优美点,这就不适合做参考资料了,太多的书写这些玩意了,而且写了很多,所以我准备是力求要点覆盖全面,语言简单明了,每个要点配个例子,说明下什么场合使用就ok了。深入研究那是要有很多实战经验和参考学习很多资料,那太累了,写的多,自己看都没有兴趣了

行列转换和增强的group by那块就要参考老兄你的啦,可以吧,我会加上引用的,嘿嘿!

[ 本帖最后由 dingjun123 于 2008-10-21 21:30 编辑 ]

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
19#
发表于 2008-10-21 22:24 | 只看该作者
支持楼主!
我粗略看了一下,有几个小意见:
NOT EXISTS 不可以代替NOT IN, 它们不是等价的;

5.2.5 层次查询限制
我在10G下试验了一下,结果发现可以JOIN, 可以用有JOIN的VIEW, CONNECT BY 可用子查询即 CONNECT BY col = (SELECT ...)

还有关于WHERE的说法:
3.Where条件限制了查询返回的行,但是不影响层次关系,属于将节点截断,但是这个被截断的节点的下层child不受影响。

WHERE其实是对查询结果进行过滤,这里最好不要用“截断”。

使用道具 举报

回复
论坛徽章:
44
2010广州亚运会纪念徽章:壁球
日期:2010-12-31 18:05:432012新春纪念徽章
日期:2012-01-04 11:54:26红宝石
日期:2012-03-27 18:07:39ITPUB 11周年纪念徽章
日期:2012-10-09 18:09:19奥运会纪念徽章:跆拳道
日期:2012-10-17 15:30:08灰彻蛋
日期:2012-11-07 09:39:58茶鸡蛋
日期:2012-11-19 10:06:022013年新春福章
日期:2013-02-25 14:51:24劳斯莱斯
日期:2013-09-17 14:59:36问答徽章
日期:2013-11-13 14:54:11
20#
发表于 2008-10-21 22:45 | 只看该作者

支持

支持,学习中~~

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表