5. 分析函数与group by结合,进行topN查询,例如我想查询总工资前三名的部门,如下:
8.5 使用场景
逻辑比较复杂的查询,往往需要用到分析函数。
9 rownum
9.1 学习目标
理解oracle内部rownum的原理,会使用rownum进行top-N查询和其他范围查询。
9.2 要点
1. rownum 和where 在同一层查询中,where 条件之后使用rownum 比较,只能使用<=,<,!=,<>,不能使用>,>=(>=1是可以的,和不加效果一样),=(使用=,只能是where rownum=1才可以)。否则不返回任何数据。如果使用!=或<>,那么只是返回前n-1行,其他按照rownum工作原理推算。
2. 当rownum 和order by 在一个语句级别中(同一层)使用的时候.看这个查询的数据是否从索引中获取(或者根据索引先得到rowid然后定位行)的,如果不是,那么就是先查询出来,每行标上rownum,然后order by 将结果重新排序,那么rownum的顺序是乱的。如果排序的数据是从索引中查询的,这样结果有序。这取决于执行计划,执行计划又和oracle优化器相关。
3. 在执行语句时,有关rownum执行的顺序是这样的:
1) 执行查询操作,初始化rownum值为1。
2) 指针指向第一行,将该行的rownum标记为1。
3) 进行where条件匹配,如果where条件返回false,则抛弃行,返回true,则返回该行,并且将rownum值自增1。
4) Oracle获取下一行,将该行的rownum标记为当前rownum值。
5) 返回第三步。
9.3 语法
Rownum可以用在where条件中,如:
Select * from emp where rownum<5;
9.4 使用案例
1. Top-N查询:
2. 查询中间几行:
看一下oracle执行的原理,当指针移动到第一行的时候,rownum=1,rownum between 3 and 5返回false,第一行被抛弃,指针指向第二行,此时rownum还是为1,第二行也被抛弃,以此类推,无结果返回。这种情况可以使用子查询,先把rownum最为rn字段缓存到结果集里面,然后对结果集进行筛选:
3. 上述查询其实存在隐患,不知道大家注意没有?子查询中select rownum rn,empno from emp order by empno,oracle的执行顺序是先取到结果集,标记上rownum,然后进行排序,这样rownum的序号不一定是排序后的序号,所以取到的数据可能不是我想要的。那此处为什么我能取到正确的数据呢?这取决与执行计划,在emp表中,empno作为表的主键,也就是唯一索引,在取得子查询结果集得时候,oracle是根据索引读取数据的,而索引一般是在oracle的内存中,并且索引是有序的,优化器选择索引的方式访问emp表,所以oracle读取数据的同时为当前行标记上rownum,所以rownum顺序不会错乱。如果我是通过ename排序取3-8行,emp在ename上并没有建立索引,所以读取的数据时错乱的,如下:
此时再用外层查询取得rn between 3 and 8,就会取得错误的数据。
此时能够保证数据是正确的。有关案例2的order by和rownum要慎用,因为即使在相关字段上有索引,oracle的优化器也不一定会选择索引访问数据,这根表的状态和其他很多原因都有关系,有关索引和执行计划的相关知识,这里不做讲解,将会在以后的课程中放在oracle优化的科目中进行讲解。
9.5 使用场景
在进行top-N查询或者取中间数据时可能用到。
10 rowid
10.1 学习目标
了解rowid的组成部分,会使用rowid进行删除重复行等查询。
10.2 要点
1. rowid的是基于64位编码的18个字符,由数据对象编号+文件编号+块编号+行编号组成(数据对象编号(6) +文件编号(3) +块编号(6)+行编号(3)=18位)。
10.3 语法
10.4 使用案例
1. Rowid经常用于删除重复行,如我用如下语句加入escore2两条重复数据,如下:
insert into escore2 select * from escore2 where sid=1;
commit;
很显然,圈出的数据为重复数据,如下我可以用rowid来删除重复数据:
delete from escore2 where rowid not in(
select min(rowid) from escore2 group by sid ,type,score
);
commit;
再来看一下escore2表,重复数据没有了:
2. 分页,例如我想取escore2表的第4-6条数据,也就是上图4、5、6行,如下:
10.5 使用场景
Rowid可用于删除重复数据或者分页,还可以用于其他的需要唯一标识行的sql。
11 Dade的使用
11.1 学习目标
掌握date数据类型,会使用date类型,并且掌握date类型的常用函数。
11.2 要点
1. 一些常用的数据格式要牢记,他们就像date对象的属性,当你要访问date对象的相应属性时,需要将这个属性作为参数传入,属性对应的值才能被返回,如:
1) Y或YY或YYY 年的最后一位,两位或三位
2) Q 季度
3) MM 月份
4) Month 用9个字符长度表示的月份名
5) WW 当年第几周
6) W 本月第几周
7) DDD 当年第几天
8) DD 当月第几天
9) D 周内第几天
10) DY 中文的星期几
11) HH或HH12 12进制小时数 HH24 24小时制
例如今天是2011年7月18日,星期一,执行如下sql看一下结果:
2. 一些常见函数的用法:
1) 对于to_date和to_char函数大家应该很熟悉了,这应该是oracle里面最常用的函数了,如下:
需要强调一点的是,oracle有默认的显示格式,对于这个格式的字符串,oracle是可以识别的,并且能通过隐式转换将其转换为date类型,如下sql(在命令行执行):
由以上结果的输出可以看出我当前数据库的时间匹配格式是18-JUL-11的,那oracle可以接收这种类型的字符串将其隐式转换为date类型,如下sql:
如下我修改了当前session的默认时间格式,则执行不报错,但是只在当前session有效:
2) Last_day(mydate),此函数返回mydate所在月份的最后一天。
3) Add_month(mydate,n),返回mydate推后n个月后的日期。
4) Months_between(date1,date2),返回date1与date2间隔的月数。
5) Next_day(mydate,dayofweek),返回自mydate日期起,下一个dayofweek(星期几)的日期。
6) Trunc(mydate,格式字符串),返回对mydate截断到指定位置后的日期。Round(mydate,格式字符串),返回对mydate四舍五入到指定位置的字符串,如下sql:
11.3 使用场景
对于日期的计算需要用到日期函数,如:
1) 上个月末:trunc(sysdate,'mm')-1
2) 本月最后一秒:trunc(last_d