有道云笔记 连接:
http://note.youdao.com/share/?id=242091c1298baddb14abb4b7b909bf35&type=note
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下可忽略:
Oracle日常操作的命令分为两大类:sqlplus命令和一般sql语句。
示例的情况:
CREATE USER scott IDENTIFIED BY m123;
GRANT DBA TO scott;
【一般查询】:
1.求员工的年工资。
错:SELECT sal*13+comm 年工资,ename FROM emp;
对:SELECT sal*13+
nvl(comm,0) 年工资,ename FROM emp;
2.查询1980年4月2日后入职的员工名单。
两种方式:SELECT hiredate, ename FROM emp WHERE hiredate > to_date('1980-4-2', 'yyyy-mm-dd');
或SELECT hiredate, enameFROM emp WHERE hiredate > '2-4月-1980';
其中, '
2-4月-1980'是oracle能默认识别的Date格式。
3.查询工资在2000至2500的员工名单。
SELECT sal 工资,ename FROM emp WHERE sal>2000 AND sal<2500;
注意条件之间别忘了and。
4.查询名字第三个字母为大写字母O的员工名单。
SELECT ename FROM emp WHERE ename LIKE '__O%';
like操作符:%表示任意0到多个字符。_表示任意单个字符。
这个例子中,注意O后面的%不要落写了。
5.查询员工号码为123或324或550的员工名单。
SELECT * FROM emp WHERE empno IN(123,324,550);
查询多个条件时建议用
in( ,, ),而不是多个or。
因为oracle对in()这种方式的查询有优化,效率特别高。
6.查询工资高于500,或岗位为MANAGER,并且姓名首字母为大写J的员工名单。
SELECT sal,job,ename FROM emp
WHERE (sal>500 OR job='MANAGER')
AND ename LIKE 'J%';
注意
括号。
>[ORDER BY]:
1.按薪水从低到高排列员工信息。
SELECT * FROM emp ORDER BY sal;
注意ORDER BY
默认(ASC)是从小到大(升序)的顺序;从大到小(降序)须加上
DESC。
2.按部门号升序而员工工资降序排列员工信息。
SELECT * FROM emp ORDER BY deptno,sal DESC;
多个排序字段,则用逗号隔开,前面的为第一排序字段,后面的一次降级。
比如此例,若是ORDER BY sal DESC,deptno,则结果完全不一样。
3.按部门号升序而员工入职时间降序排列员工信息。
只需将上例的sal改为hiredate。
注意,Oracle中日期、时间
(Date)字段可排序,即有大小,应该是时间戳形式的。
4.按年薪降序排序(或者说,查询员工的年薪并按从高到低排序)。
SELECT sal*12+NVL(comm,0) "年薪",ename FROM emp
ORDER BY "年薪" DESC;
这里用到了
Oracle的“别名”:
a.别名可以是中文或英文,都是用
半角双引号(一大特点);
b.别名钱可加as,也可省略;
c.别名作为整体可作为ORDER BY的字段;
类似本例这种带别名的排序查询,在统计系统、财务系统经常使用。
【复杂查询_统计类】:
复杂SELECT基本分为
两类:
a.数据统计。体现在GROUP BY及相关的数据分组查询。
b.连接查询。多表相关联。
另外一个维度,就是逻辑复杂,子查询嵌套。
结果:单看一个复杂SELECT,很难读懂。
>[合计函数]:
合计函数(或者叫,聚合函数):MIN,MAX,AVG,SUM,COUNT。
它们的特点是用在多条记录上,用于统计。
合计函数不一定与GROUP BY一起使用。
1.显示所有员工的最低工资、最高工资。
SELECT MIN(sal) "最低工资",MAX(sal) "最高工资" FROM emp;
2.显示所有员工的平均工资和工资总和。
SELECT AVG(sal) "平均工资",SUM(sal) "工资总和" FROM emp;
3.显示共有多少员工。
SELECT COUNT(empno) FROM emp;
4.显示职位有多少种。
错:SELECT count(job) "职位种数" FROM emp;
对1(简单
但不建议):SELECT count(
DISTINCT job) "职位种数" FROM emp;
对2:SELECT COUNT(
"tmp") "职位种数" FROM
(SELECT
MIN(sal) "tmp" FROM emp
GROUP BY job);
如此例,
替代DISTINCT(因为它降低效率)的方法:
a.先用GROUP BY对需要去重的字段做一次查询,SELCET后是任意的聚合函数(越简单越好),病取个别名;
b.对上面的子查询进行查询,SELECT后是对子查询结果的那个别名的聚合函数。
5.显示工资最高员工的工资,名字,工作岗位。
错:SELECT MAX(sal),ename FROM emp;
对:SELECT sal,ename FROM emp WHERE sal=(SELECT MAX(sal) FROM emp);
看似简单,但是却需要使用子查询。
而且
注意到(Oracle的“道理”),
a.分组函数一般不能跟一般字段
混用作为SELECT的结果集,除非GROUP BY中。
b.子查询的结果是一个数值,可以
赋值给查询条件。
6.显示工资高于平均工资的员工信息。
SELECT * FROM emp WHERE sal>(SELECT AVG(sal) FROM emp);
与上例同理。
7.将工资低于平均工资、又是1980年12月31日前入职的的员工工资涨10%(一条SQL搞定)。
UPDATE emp SET sal=sal*1.1 WHERE sal<(SELECT AVG(sal) FROM emp)
AND hiredate<to_date('1980/12/31','yyyy/mm/dd');
此例算是比较复杂的SQL了。
>[GROUP BY及HAVING子句
]:
1.显示每个部门(号)的平均工资,最高工资。
SELECT AVG(sal),MAX(sal),deptno FROM emp GROUP BY deptno;
2.显示每个部门的各个岗位的平均工资和最高工资。
SELECT AVG(sal),MAX(sal),job,deptno FROM emp GROUP BY deptno,job;
多层分组:按顺序由逗号分开。(而不是子查询)
3.显示平均工资高于2000的部门号和它的平均工资。
SELECT AVG(sal),deptno FROM emp GROUP BY deptno having AVG(sal)>2000;
HAVING子句:SQL中增加它就是为了解决WHERE子句无法
与合计函数一起使用的问题。
4.上例,并按从高到低排序。
SELECT AVG(sal),deptno FROM emp GROUP BY deptno HAVING AVG(sal)>2000 ORDER BY AVG(sal) DESC;
有以下固定顺序(经验):
GROUP BY-->HAVING-->ORDER BY
注意的是,
合计函数能出现在SELECT列表、HAVING子句、ORDER BY子句中,
唯不能出现在查询条件WHERE子句中。
【复杂查询_多表】:
实际项目中,更多的是多表查询。原因,一是设计数据库时,避免数据冗余,一个表的结构不应该复杂(字段之间有关联则不合范式);二是,实际的需求逻辑复杂,任何SQL只是工具。
笛卡尔积bug:
a.像上面的连接查询(不加条件的笛卡尔积),是很危险的,项目中不可能出现,尽管能查出来结果(
病毒?瘫痪数据库服务器)。比如再连接一张5条记录的表,结果就有14*4*5=280条。
b.2张表则至少有1个连接条件,3张表至少2个连接条件......即
n张表连接查询,则至少有n-1个连接条件。在做复杂多表连接查询时,这条可以作为一个验证,不会出上一条类似的bug。
注意:n-1个连接条件仍不能保证笛卡尔积bug消失,连接条件不合理的话。
1.显示部门号为10的部门地址、员工名和工资。
连接查询写法:
3.显示各个员工的姓名、工资及其工资级别。
写法步骤跟上一例一样,但注意到:emp和salgrade没有主外键关系。
注意:所以,直观地,两个表做连接查询,
可以想象成两表先做笛卡尔积,然后WHERE子句筛选。(这样,多表连接、自连接等就很好理解了)
4.显示雇员名、雇员工资及所在部门的名字,并按部门排序。
5.显示员工FORD的上级领导姓名。
【复杂查询_子查询】:
1.显示SMITH的所有同部门同事姓名。
注意的是:子查询可能嵌套很多层,但不一定同时是连接查询。
补充:项目中设计表时,不建议把逻辑拆分得太复杂(视图?),这样会造成一个SELECT语句过于复杂(5张表以上的连接才行),不利于
他人开发和维护(读复杂SQL很费时间,可能过段时间自己都读不懂了)
。一般设计为连接的表数量在5个以内。
2.显示所有工作为SALEMAN员工的部门的MANAGER。
3.显示比部门号30的所有部门员工工资都高的员工姓名、工资、部门名。
本例中的多行子查询加ALL可以用合计函数MAX的一种方式替换。而且
推荐后一种,因为效率可能会高一点(比较次数少)。“能快一秒,绝不慢一秒。快0.01秒都是高手(韩顺平)”
注意到,连接查询的条件a1.deptno=a2.deptno和其它条件的先后顺序不重要。可参考后面的优化技巧-->8-->Oracle扫描顺序-->实验结果。
4.将题目改为"显示比SALES部门的所有部门员工工资都高的员工姓名、工资、部门名"。
可
描述为:2表连接查询+多行子查询(包含2表连接查询,且是同2张表)。相当于对"2表连接查询"做了一次自连接+子查询。
写起来逻辑不复杂,只要别把别名弄混了。但是若让另个人来读,难度可能就大了。(尤其别名起的不好的情况下)所以,
别名对复杂SQL语句的
可维护性很重要。
5.又将题目改为"显示比
部门号30部门
的
任意
员工工资都高的员工姓名、工资、部门名"。
SELECT a1.ename,a1.sal,a2.dname FROM emp a1,dept a2
WHERE a1.deptno=a2.deptno AND
a1.sal>
ANY(SELECT sal FROM emp WHERE deptno=30) ;
SELECT a1.ename,a1.sal,a2.dname FROM emp a1,dept a2
WHERE a1.deptno=a2.deptno AND
a1.sal> (SELECT
MIN(sal) FROM emp WHERE deptno=30) ;
多行子查询用到ANY。ANY跟ALL正好是个对比,就像MIN和MAX的对比一样。
6.显示与SMITH的部门和岗位完全相同的所有雇员。
重点在,WHERE子句后可有
WHERE (deptno,job)=(......)这种形式。
7.显示高于自己部门平均工资的员工名单。
这是所谓
需要“编程”的SQL语句。
若要完成题目,则需要依题构造一个子查询,即:
说“构造”,是因为这个查询需要有deptno和AVG(sal)两项,以便与emp表由deptno建立连接查询。
这个FROM后手工构造的子查询,术语叫“
内嵌视图”。它必须
有别名,这个别名相当于视图名。
补充(别名):Oracle有一些奇怪又复杂的规定,比如给列起别名可以加as,给表起别名不能加as;再比如列的别名是中文时,可加双引号或不加都可以,但是英文时可能对单引号、双引号和不加引号有区别。
这可能跟Oracle几十年来版本太多,新版本极力支持老版本,导致系统架构不清有关。Java也有这个趋势,“一切为对象”的最初思想,因为各新版本增加的自动拆装箱、泛型、闭包等函数编程概念,变得模糊起来。
所以,对于这些地方,千万别较真,只有多积累经验,能记住几条就是几条,就足够了。
【综合_分页Pagination】:
Oracle的分页比其它数据库都不好理解,要会子查询才能写好。
由于有好几种写法,而且写法复杂,所以单纯靠记公式(MySql或2000)是行不通的。要知道两条:
ROWNUM的机制和
子查询。
不同的分页写法,对效率有影响。
由于这个地方Oracle没有个标准,所以"到时候能写出来就可以了,老板知道你干什么的就可以了"。
Oracle的领域许多都是,
首要是把需求实现出来,然后再一点点优化。因为没有人能从根本上说这条SQL语句效率就是最高的。
1.分页查询的几个写法。
方法1(易记,易读,效率高,易维护):
方法2(最常见):
此写法的分页,很多人都是这么写(将大于和小于两个条件
分别放在主查询和子查询中)。
容
易出错的地方:子查询的SELECT列表中ROWNUM必须有别名rn(主查询要用),但是子查询的条件不能用别名rn<6,只能ROWNUM<6。
补充:这个方法产生的思路(非子查询SELECT ROWNUM,ename FROM emp where ROWNUM>=3
AND ROWNUM >=6在Oracle中出错),参考:
笔记_查询优化,及一些细节-->6.查找第x行数据-->a.非子查询方式-->实验。
方法3(项目推荐,易维护):
方法3是
方法2的延伸(思路都是将大于和小于分开存放),它的特点在于:
结果集方便控制。
比如,如果想分页结果集是(rn,ename,sal)这3项,只需把最里层子查询b1的*改为ename,sal。
效率实验:
过程略吧。存了990条数据,三个方法试过后,可能缓存的关系,效率差别不大。
实际工作中,选哪种方式,自己具体情况具体试一试,只要能够说服就行。
注意的是,结果集的选取,对效率影响旺旺更大。
方法4(ROWID分法,效率最高):
韩顺平的效率试验:
问题是ROWID方法太容易写错了,也不好维护。
韩顺平的效率试验:
问题是ROWID方法太容易写错了,也不好维护。
2.分页查询的排序。
注意到,分页查询的排序有
两种:
a.先把所有记录排序,再取第100到第110条;
b.取到第100到110条后,再对这10条排序。
一般,如果需求明确是第一种,那么很简单,写分页时
注意下ORDER BY的位置。如果不明确是第一种,那么一定不要按第一种写,因为效率会低。
【综合_建表由SELECT】:
这个在Oracle的维护、实验中有用。因为数据很珍贵吧,所以试一试增删改查,先把表用这个方法导出新建一份,在这个上测试。
【综合_SELECT的集合】:
这个在实际项目中用的不多,了解有
UNION(合集)、
UNION ALL(合集,包括重复)、
INTERSECT(交集)、
MINUS(差集)这几个集合运算就行。
补充:但是,韩顺平听网上说,大数据量时,这4个集合操作,
效率比WHERE(包括AND,OR)效率高出很多。
对于这种传说中的查询优化,留心并记下来就可以了。因为数据量不大时,效率差基本看不出来。等到数据量大、数据库吃紧时,又能想到
优化的方向,足够。