本篇内容较多大家可以分几次看
1.Oracle简介
公司“甲骨文”,全球第一大数据库数据库厂商,第二大独立软件供应商
一:发展史
1970年 数据库起家
1980年 ORACLE6版本
1990年 ORACLE7版本,多元化产品,以数据库为优先发展方向
1995年 ORACLE8版本
1999年 ORACLE8i版本
2001年 ORACLE9i release1
2002年 ORACLE9i release2
2003年 ORACLE10g版本
2007年 ORACLE11g版本
2009 年 甲骨文以每股9.5美元价格收购Sun公司总交易额74美元
二:近些版本的特点
Oracle10g
1)支持网格计算(grid)达到负载均衡:多台节点服务器利用高速网络组成一个虚拟的高性能服务器,负载在整个网格中均衡,避免单点故障
2)安装比9i工作量减少一半
3)新增浏览器的企业管理器(Enterprise Manager)
Oracle11g
.........
Oracle12g
........
2.Oracle安装
一:系统要求(一般我们现在的电脑都满足这些条件)
操作系统最好是windows server
内存最好是256M
硬盘空间最好是2G以上
二:官网下的
3.Oracle卸载
一:停止所有与Oracle相关的服务
二:使用OUI(Oracle Universal Installer)卸载Oracle软件。
开始->程序->Oracle-OraDb110g_home1|Oracle installation product|Universal installer.
三:删除注册表中的内容运行regedit命令,删除下面内容:
HKEY_LOCAL_MACHINE|SOFTWARE|ORACLE注册表键,删除此键。
HKEY_LOCAL_MACHINE|SYSTEM|CurrentControlSet|Services,删除Services键下所有以oracle为首的键。
HKEY_LOCAL_MACHINE|SYSTEM|CurrentControlSet|Services|Eventlog|Application, 删除此键下所有以oracle为首的键。
HKEY_CLASSES_ROOT,删除此键下所有以Ora,Oracle,Orcl,EnumOra 为前缀的键。
HKEY_CURRENT_USER|Software| Microsoft|Windows|CurrentVersion|Explorer|MenuOrder|Start Menu|Programs, 删除此键下所有以oracle为首的键。
HKEY_LOCAL_MACHINE|SOFTWARE|ODBC|ODBCINST.INI注册表键,删除了Microsoft ODBC FOR ORACLE注册表键以外的所有有Oracle字样的键值。
HKEY_LOCAL_MACHINE|SYSTEM|CurrentControlSet|Services,删除以Oracle或OraWeb为前缀的键。
三:删除环境变量,PATH中含有Oracle的位置信息都删了
四:文件系统中删除Oracle相关的文件与目录(app目录,CProgram FilesOracle)
如果C盘下的Oracle删不了重启计算机即可
4.Oracle学习
一:注意Oracle安装会自动的生成sys用户和system用户:
sys用户是超级用户,具有最高权限,具有sysdba角色,有create database的权限,该用户默认的密码是change_on_install
system用户是管理操作员,权限也很大。具有sysoper角色,没有create database的权限,默认的密码是manager
一般讲,对数据库维护,使用system用户登录就可以拉
也就是说sys和system这两个用户最大的区别是在于有没有create database的权限。
二:管理工具
Sql*plus
在开始->程序->oracle oradb_home10g->application development->sql*plus
在运行栏中输入: sqlplusw即可
Sqlplus
Dos下的Oracle管理工具功能与sql*plus相似
在运行栏中输入 sqlplus;在oracle主目录ora10ginsqlplus.exe
Pl/sql developer
第三方软件,主要用于开发,测试,优化oracle
Enterprise manager console(企业管理器)
Web管理Oracle,一般端口号为1158或5500
URL:http://机器名:端口/em
三:基本命令
Sql*plus常用命令
1)连接命令
连接数据库:Conn[ection] 用户名/密码@网络服务名 [as sysdba[sysoper]]
断开数据库:disc[onnect]
修改密码: passw[ord]
退出:exit
2)文件操作命令
Start /@:运行sql脚本
sql>@ d:a.sql或是sql>start d:a.sql
Spool 将命令开始到spool off间的内容输出到指定文件中
Sql>spool 要存放的文件地址(此时开始记录命令窗口中的命令)-》spool off(存到指定文件的命令结束此时查看文件发现出现了很多命令)
Edit:编辑sql文件
3)交互式命令
&可以弹出窗口让我们自己填
Select * from emp where job=’&job’;
Spool:将命令窗口中的命令存到指定文件中
4)显示设置环境变量
用来控制输出的各种格式如果希望永久的保存相关的设置,可以去修改glogin.sql脚本
Linesize:set/show Linesize 查看设置输出一行宽度默认80
Pagesize: set/show Pagesize 查看设置输出一页行数默认14
4)用户管理
注意:权限,角色,数据对象的解释
权限分为系统权限,对象权限;系统权限:对数据库的相关操作(建用户,赋权限等),对象权限对数据对象的相关操作(select,insert,update,delete,all);
角色是将权限封装在一起
1)创建用户
创建用户需要有dba权限才可以做(sys/system用户)
A.简单方式
Create user 名字 identified by 密码
B.复杂方式
Create user 名字 identified by 密码
Default tablespace 默认表空间名
Temporary tablespace 临时的表空间名
Quota 最大用户可创建数据对象(表,索引,视图)
2)修改密码
自己修改密码没问题;别人修改密码需要有dba权限或者有修改用户的系统权限
自己:Password 用户名
别人:alter user 用户名 identified by 新密码
3)删除用户
Dba权限或者有alter user系统权限
Dorp user 用户名【cascade】如果用户创建过表需要加上cascade
4)赋权限
希望xiaoming用户可以去查询emp表
希望xiaoming用户可以去查询scott的emp表
grant select on emp to xiaoming
希望xiaoming用户可以去修改scott的emp表
grant update on emp to xiaoming
希望xiaoming用户可以去修改/删除,查询,添加scott的emp表
grant all on emp to xiaoming
scott希望收回xiaoming对emp表的查询权限
revoke select on emp from xiaoming
小明也想将对象权限(select on Scott.emp)分配给xiaohong
Scott 给xiaoming赋权限时加with grant options
grant select on emp to xiaoming with grant option;
如果Scott将对象权限从xiaoming这撤销了,小红从xiaoming那获得对象权限也就没有了
5)使用profile管理用户口令
Profile:是口令限制,资源限制的命令集合,当建立数据时,Oracle会自动创建default默认的profile文件,当创建用户如果没有指定profile,系统会默认指定default的profile文件
A.账户锁定
概述:指定scott用户最多只能尝试三次登录,锁定时间2天
SQL>create profile lock_account limit failed_login_attempts 3 password_lock_time 2;
SQL> alter user scott profile lock_account; B账户解锁
Alter user 被锁定的用户名 account unlock;
C.终止口令
概述:要用户每隔10天修改自家密码,宽限期2天
SQL> create profile myprofile limit password_life_time 10 password_grace_time 2;
SQL> alter user 用户名profile myprofile;
D.口令历史
概述:当希望用户在修改密码时不使用以前的密码可使用口令历史,这样Oracle就会将口令的修改存到口令字典中,下次再更改密码时就会与旧密码作对照
SQL>create profile password_history limit password_life_time 10 password_grace_time 2 password_reuse_time 10
password_reuse_time //指定口令可重用时间即10天后就可以重用
E.删除profile
SQL> drop profile password_history 【casade】
注意:文件删除后,用这个文件去约束的那些用户通通也都被释放了。
加了casade,就会把级联的相关东西也给删除掉
------------------------------------------休息一下------------------------------------------------
四:表管理
1)创建表
Create table 表名(field1 datatype,field2 datatype)
A:数据类型解析
分类 | 数据类型 | 说明 |
文本,二进制类型 |
char(20) varchar2(20) nchar(20) nvarchar2(20) clob(character large object) blob(binary large object) |
定长 最大2000字符 变长 最大4000字符 Unicode数据类型 ,定长 最大2000字符 Unicode数据类型 ,变长 最大4000字符 字符型大对象 ,最大8tb 二进制数据 可以存放图片/声音 8tb |
数值类型 | number(p,s) | p:整数位;s:小数位
范围: 1 <= p <=38, -84 <= s <= 127 保存数据范围:-1.0e-130 <= number value < 1.0e+126 保存在机器内部的范围: 1 ~ 22 bytes |
时间日期 |
data timestamp(n) |
包含年月日,时分秒。默认格式:DD-MON-YYYY。从公元前4712年1月1日到公元4712年12月31日的所有合法日期 n的取值为0~9.表示指定TIMESTAMP中秒的小数位数。N为可选。如果n为0,timestamp与date等价[不推荐] |
表名和列的命名规则
必须以字母开头
长度不能超过30个字符
不能使用oracle的保留字
只能使用如下字符 A-Z,a-z,0-9,$,#等
数值类型小习题:
例子:
1.创建两个表 create table student ( ---表名 xh number(4), --学号 xm varchar2(20), --姓名 sex char(2), --性别 birthday date, --出生日期 sal number(7,2) --奖学金 ); --班级表 CREATE TABLE class( classId NUMBER(2), cName VARCHAR2(40) ); 2.修改表 添加一个字段 alter table student add(classId number(2)); 修改一个字段长度 alter table student modify(xm varchar2(30)); 修改字段类型或者名称(不能有数据)不建议这么做 alter table student modify(xm char(30)); 删除一个字段 不建议这么做因为删了顺序就变了,加就没问题,加是加在后面 alter table student drop column sal; 修改表名 很少有这种需求 rename table student to stu; 删除表 drop table student;
3.添加数据
所有字段都插入数据
修改日期的默认格式(临时修改,数据库重启后仍为默认;如要修改需要修改注册表)
修改后,可以用我们熟悉的格式添加日期类型:
插入部分字段n
插入空值
如果要查询student表中birthday为null的记录怎么写sql?
4.修改数据
用Update关键字
5.删除数据
Delete:删除数据结构还在数据还可以恢复因为它写日志所有速度慢,只需创建还原点savepoint a;处理的操作;rollback to a;
drop:删除数和结构不写日志所以速度快,不能恢复
五:查询
新安装的数据库中scott用户拥有的表(emp,dept)我们可以那他们做例子
1)oracle表基本查询
1.查看表结构
desc emp;
2.查询指定列
这里值得一说的是distinct函数消除重复行
3.打开显示操作时间的开关
set timing on(测试后发现不能用不知道怎么回事)
4.nvl,显示每个雇员的年工资
思路:每位雇员每月可能还有奖金可能没有就出现null值解决null的问题就需要用到nvl函数了 解决:select a.sal*13+nvl(a.comm,0) "年薪",a.ename,a.comm from emp a
5.like使用:%:表示0到多个字符 _:表示任意单个字符
6.where条件中使用in
SELECT * FROM emp WHERE empno in (7844, 7839,123,456);
7.is null
SELECT * FROM emp WHERE mgr is null;
8.order by 字段:排序默认asc
问题:按照部门号升序而雇员的工资降序排列
SELECT * FROM emp ORDER by deptno, sal DESC;
还可以使用列的别名来排序
select ename, (sal+nvl(comm,0))*12 "年薪" from emp order by "年薪" asc;
9.clear清屏
2)oracle复杂查询
1.数据分组--max,min,avg,sum,count 注意:select ename, max(sal) from emp;这语句执行的时候会报错,因为那里需要一样max是分组函数,ename不是 2.group by 和having子句 group by 对查询结果分组统计,having限制分组显示结果 问题:如何显示每个部门的平均工资和最高工资?
思路,每个部门明显分组用到group by select max(sal),min(sal),a.deptno from emp a group by a.deptno;
问题:显示每个部门的每种岗位的平均工资和最低工资?
思路:每个部门的每种岗位两个分组 select max(sal),min(sal) ,a.empno,a.job from emp a group by a.empno,a.job
问题:显示平均工资低于2000的部门号和它的平均工资?
SELECT AVG(sal), MAX(sal), deptno FROM emp GROUP by deptno having AVG(sal) < 2000;
对数据分组的总结n
分组函数只能出现在选择列表、having、order by子句中(不能出现在where中)
如果在select语句中同时包含有group by, having, order by 那么它们的顺序是group by, having, order by
在选择列中如果有列、表达式和分组函数,那么这些列和表达式必须有一个出现在group by 子句中,否则就会出错。
如SELECT deptno, AVG(sal), MAX(sal) FROM emp GROUP by deptno HAVING AVG(sal) < 2000;
这里deptno就一定要出现在group by 中
3.多表查询
规定:多表查询的条件是 至少不能少于 表的个数-1 才能排除笛卡尔集
问题:显示部门号为10的部门名、员工名和工资?
思路:两表查询,两表建立连接,一个成员在莫一段位置 select a.ename,a.sal,b.grade from emp a,salgrade b where a.sal between b.losal and b.hisal;
问题:显示雇员名,雇员工资及所在部门的名字,并按部门排序?
select a.ename,a.sal,b.dname from emp a,dept b where a.empno=b.deptno order by a.empno
(注意:如果用group by,一定要把e.deptno放到查询列里面)
4.自连接
同一张表的连接查询
问题:显示某个员工的上级领导的姓名?
思路:这两表关键就是a表的mgr和b表ename要相同 SELECT worker.ename, boss.ename FROM emp worker,emp boss WHERE worker.mgr = boss.empno AND worker.ename = 'FORD';
5.子查询
请思考:如何查询和部门10的工作相同的雇员的名字、岗位、工资、部门号
select * from emp where job in(select distinct job from emp a where a.deptno=10)
A:多行子查询
在多行子查询中使用all操作符
问题:如何显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号?
select * from emp a where a.sal>all(select sal from emp b where b.deptno=30 )
执行效率上, 函数高得多
在多行子查询中使用any操作符
问题:如何显示工资比部门30的任意一个员工的工资高的员工姓名、工资和部门号?
SELECT ename, sal, deptno FROM emp WHERE sal > ANY (SELECT sal FROM emp WHERE deptno = 30);
B:多列子查询(查询结果多个列)多列多行查询的区别是:查询条件是一列和多列的区别
请思考如何查询与SMITH的部门和岗位完全相同的所有雇员。
select * from emp a where (a.deptno,a.job)=(select deptno, job from emp where ename='SMITH' )
如何显示高于自己部门平均工资的员工的信息(很难的多想想)
思路:看到这个题目一般大家都是蒙的,需要查分下,这里肯定是有子查询的,找到子查询,“自己部门平均工资”即可变为查询各个部门平均工资—》之后再原表与此表多表查询就很简单了 select a.ename,a.sal,b.mysal from emp a,(select avg(sal) mysal,deptno from emp group by deptno) b where a.deptno=b.deptno and a.sal>b.mysal
注意:(给表取别名的时候,不能加as;但是给列取别名,是可以加as的)
3)分页查询
按雇员的id号升序取出
oracle的分页一共有三种方式
1.根据rowid来分
select * from t_xiaoxi where rowid in (select rid from (select rownum rn, rid from(select rowid rid, cid from t_xiaoxi order by cid desc) where rownum<10000) where rn>9980) order by cid desc;
执行时间0.03秒
2.按分析函数来分
select * from (select t.*, row_number() over(order by cid desc) rk from t_xiaoxi t) where rk<10000 and rk>9980;
执行时间1.01秒
3.按rownum来分
select * from (select t.*,rownum rn from(select * from t_xiaoxi order by cid desc)t where rownum<10000) where rn>9980;
执行时间0.1秒
应用最多的是rownum来分比较容易理解
例如工资排序后查询6-10条数据
select * from (select a.*,rownum rn from (select * from emp order by sal) a where rownum<=10) where rn>=6
这里要先截取大行再封住小行从而获得分页效果
4)自我复制数据(蠕虫复制)
insert into mytable (id,name,sal,job,deptno)
select id,name,sal,job,deptno from mytable;
执行多次就能获得大数据
6.合并查询
多用于数据量大的合并多个查询结果
union,union all,intersect,minus
Union:保留相同的;
Union all:全部保留;
Intersect:取得两个结果集的交集
Minus:相当于做减法
7.内连接外连接
内连接:where两表查询就相当于内连接
外连接:left jion,right jion,
8.创建数据库实例
1). 通过oracle提供的向导工具。√
database Configuration Assistant 【数据库配置助手】
2).我们可以用手工步骤直接创建。
------------------------------------休息一下----------------------------------------------------
1.java操作Oracle
jdk从1.8开始,删除了jdbc-odbc桥,所以odbc的驱动是用不了的,建议重新安装jdk1.7或者更低的版本
加载驱动之前需要导入jar包安装Oracle后在D:OracleappAdministratorproduct11.2.0dbhome_1jdbclib中找ojdbc.jar
//加载驱动 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //得到连接 Connection ct=DriverManager.getConnection("jdbc.odbc:testConnectOracle","scott","scott"); //这里过后与SQL Server查询一样了 Statement sm=ct.createStatement(); ResultSet rs=sm.executeQuery("select * from emp"); while(rs.next()){ System.out.println("用户:"+rs.getString(2)); }
再创建连接之前需要配置ODBC点击控制面板-->系统和安全-->管理工具-->数据源(ODBC),打开后点添加,如图:
可以看到,有个Oracle in OraDb10g_home1的驱动,它e安装完后自动加上去的。 选中后,点完成,再填如下信息,如图:
这样配好后基本就可以了,但为了安全起见,建议大家测试一下,点击 Test Connection按钮, 测试通过后点ok,然后数据源就生成了图:
然后把数据源名称写进jdbc.odbc:里。
这里要注意:jdbcodbc能不能远程连接呢?不能远程连接,也就是你这样写的话就意味着java程序和oracle数据库应该是在同一台机器上,因为这里没有指定IP地址,肯定默认就是本地。如果要远程连,就用jdbc,jdbc是可以远程连的。
//jdbc模式可以远程连接的
//加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); //得到连接 Connection ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orclbc:testConnectOracle","scott","scott"); //这里过后与SQL Server查询一样了 Statement sm=ct.createStatement(); ResultSet rs=sm.executeQuery("select * from emp"); while(rs.next()){ System.out.println("用户:"+rs.getString(2)); }
2.使用特定的格式插入日期值
To_data(‘1998-09-12’,’YYYY-MM-DD’)
3.Oracle中的事务处理
当执行事务操作时(dml语句),oracle会在被作用的表上加锁,防止其它用户修改表的结构。这里对我们的用户来来讲是非常重要的。
.....其它进程排序,知道1号进程完成,锁打开,2号进程进入。依次进行,如果有进程级别较高的,可以插队。
当执行用commit语句可以提交事务
事务的几个重要操作n
设置保存点 savepoint a
取消部分事务 rollback to a
取消全部事务 rollback
注意:这个回退事务,必须是没有commit前使用的;如果事务提交了,那么无论你刚才做了多少个保存点,都统统没有。
如果没有手动执行commit,而是exit了,那么会自动提交
例子:
Connection ct=null; try { Class.forName("oracle.jdbc.driver.OracleDriver"); ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","tiger"); //加入事务 ct.setAutoCommit(false);//设置默认不能提交 Statement sm=ct.createStatement(); sm.execute("update emp set sal=sal-100 where ename='SCOTT'"); int i=7/0; //给smith的sal加上100 sm.executeUpdate("update emp set sal=sal+100 where ename='SMITH'"); ct.commit();//提交事务 //关闭资源 sm.close(); ct.close(); } catch (Exception e) { // TODO Auto-generated catch block try { ct.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); }
设置只读事务
set transaction read only;
4.Oracle中的函数
1)字符函数
lower(char)
upper(char)
length(char)
substr(char,m,n):取字符串的子串n代表取n个的意思
replace(char1,search_string,replace_string)
instr(char1,char2,[,n[,m]])取子串在字符串的位置
2)数学函数
cos,cosh,exp,ln, log,sin,sinh,sqrt,tan,tanh,acos,asin,atan,round,
常用的:
Round(n,[m]):四舍五入
trunc(n,[m]): 截取数字
mod(m,n) :取m,n的模
floor(n) 返回小于或是等于n的最大整数
ceil(n) 返回大于或是等于n的最小整数
对数字的处理,在财务系统或银行系统中用的最多,不同的处理方法,对财务报表有不同的结果
3)日期函数
默认情况下日期格式是dd-mon-yy 即12-7月-78
Sysdata:放回系统时间
Add_months(d,n)在d日期后加n个月后的日期
last_day(d):返回指定日期所在月份的最后一天
问题:查找已经入职8个月多的员工
SQL> select * from emp where sysdate>=add_months(hiredate,8);
问题:显示满10年服务年限的员工的姓名和受雇日期。
SQL> select ename, hiredate from emp where sysdate>=add_months(hiredate,12*10);
问题:对于每个员工,显示其加入公司的天数。
SQL> select floor(sysdate-hiredate) "入职天数",ename from emp;
or
SQL> select trunc(sysdate-hiredate) "入职天数",ename from emp;
问题:找出各月倒数第3天受雇的所有员工。
SQL> select hiredate,ename from emp where last_day(hiredate)-2=hiredate;
4)转换函数
Oracle提供默认的转换但是默认的功能不足
转换函数就能解决我们的问题了
To_char()
问题:日期是否可以显示 时/分/秒
SQL> select ename, to_char(hiredate, 'yyyy-mm-dd hh24:mi:ss') from emp;
问题:薪水是否可以显示指定的货币符号
SQL>
yy:两位数字的年份 2004-->04
yyyy:四位数字的年份 2004年
mm:两位数字的月份 8月-->08
dd:两位数字的天 30号-->30
hh24: 8点-->20
hh12:8点-->08
mi、ss-->显示分钟秒
9:显示数字,并忽略前面0
0:显示数字,如位数不足,则用0补齐
.:在指定位置显示小数点
,:在指定位置显示逗号
$:在数字前加美元
L:在数字前面加本地货币符号
C:在数字前面加国际货币符号
G:在指定位置显示组分隔符、
D:在指定位置显示小数点符号(.)
问题:显示薪水的时候,把本地货币单位加在前面
SQL> select ename, to_char(hiredate, 'yyyy-mm-dd hh24:mi:ss'), to_char(sal,'L99999.99') from emp;
问题:显示1980年入职的所有员工
SQL> select * from emp where to_char(hiredate, 'yyyy')=1980;
问题:显示所有12月份入职的员工
SQL> select * from emp where to_char(hiredate, 'mm')=12;
to_daten
函数to_date用于将字符串转换成date类型的数据。
5)系统函数
sys_contextn
terminal:当前会话客户所对应的终端的标示符
lanuage: 语言
db_name: 当前数据库名称
nls_date_format: 当前会话客户所对应的日期格式
session_user: 当前会话客户所对应的数据库用户名
current_schema: 当前会话客户所对应的默认方案名
host: 返回数据库所在主机的名称
通过该函数,可以查询一些重要信息,比如你正在使用哪个数据库?
select sys_context('USERENV','db_name') from dual;
注意:USERENV是固定的,不能改的,db_name可以换成其它,比如select sys_context('USERENV','lanuage') from dual;又比如select sys_context('USERENV','current_schema') from dual;
------------------------------------休息一下---------------------------------------------
5.数据库管理,表的逻辑备份与恢复
最大区别:sysdba可以创建数据库,sysoper不可以创建数据库
sysdba>sysoper>dba
1)调试优化 数据库
管理数据库参数可以调试数据库
2) 显示初始化参数l
show parameter命令
如何修改参数
D:oracleadminmyoralpfileinit.ora文件中去修改比如要修改实例的名字
3)数据库(表)的逻辑备份与恢复
在导入和导出的时候,要到oracle目录的bin目录下。
A:导出:
导出具体的分为:导出表,导出方案,导出数据库三种方式。
导出使用exp命令来完成的,该命令常用的选项有:
userid: 用于指定执行导出操作的用户名,口令,连接字符串
tables: 用于指定执行导出操作的表
owner: 用于指定执行导出操作的方案
full=y: 用于指定执行导出操作的数据库
inctype: 用于指定执行导出操作的增量类型
rows: 用于指定执行导出操作是否要导出表中的数据
file: 用于指定导出文件名
例子:
导出表n
xp userid=scott/tiger@myoral tables=(emp,dept) file=d:e1.dmp 2.导出其它方案的表 如果用户要导出其它方案的表,则需要dba的权限或是exp_full_database的权限,比如system就可以导出scott的表 E:oracleora92in>exp userid=system/manager@myoral tables=(scott.emp) file=d:e2.emp 特别说明:在导入和导出的时候,要到oracle目录的bin目录下。 3. 导出表的结构 exp userid=scott/tiger@accp tables=(emp) file=d:e3.dmp rows=n 4. 使用直接导出方式 exp userid=scott/tiger@accp tables=(emp) file=d:e4.dmp direct=y 这种方式比默认的常规方式速度要快,当数据量大时,可以考虑使用这样的方法。 这时需要数据库的字符集要与客户端字符集完全一致,否则会报错...
- 导出自己的方案
exp userid=scott/tiger@myorcl owner=scott file=d:scott.dmp
2. 导出其它方案
如果用户要导出其它方案,则需要dba的权限或是exp_full_database的权限,比如system用户就可以导出任何方案
3.导出数据库
exp userid=system/manager@myorcl owner=(system,scott) file=d:system.dmp
增量备份(好处是第一次备份后,第二次备份就快很多了)
B:导入
imp常用的选项有
userid: 用于指定执行导入操作的用户名,口令,连接字符串
tables: 用于指定执行导入操作的表
formuser: 用于指定源用户
touser: 用于指定目标用户
file: 用于指定导入文件名
full=y: 用于指定执行导入整个文件
inctype: 用于指定执行导入操作的增量类型
rows: 指定是否要导入表行(数据)
ignore: 如果表存在,则只导入数据
exp userid=system/manager@myorcl full=y inctype=complete file=d:all.dmp 1. 导入自己的表 imp userid=scott/tiger@myorcl tables=(emp) file=d:xx.dmp 2. 导入表到其它用户 要求该用户具有dba的权限,或是imp_full_database imp userid=system/tiger@myorcl tables=(emp) file=d:xx.dmp touser=scott 3. 导入表的结构 只导入表的结构而不导入数据 imp userid=scott/tiger@myorcl tables=(emp) file=d:xx.dmp rows=n 4. 导入数据 如果对象(如比表)已经存在可以只导入表的数据 imp userid=scott/tiger@myorcl tables=(emp) file=d:xx.dmp ignore=y 导入方案n 导入方案是指使用import工具将文件中的对象和数据导入到一个或是多个方案中。如果要导入其它方案,要求该用户具有dba的权限,或者imp_full_database 1. 导入自身的方案 imp userid=scott/tiger file=d:xxx.dmp 2. 导入其它方案 要求该用户具有dba的权限 imp userid=system/manager file=d:xxx.dmp fromuser=system touser=scott 导入数据库n 在默认情况下,当导入数据库时,会导入所有对象结构和数据,案例如下: imp userid=system/manager full=y file=d:xxx.dmp
6.数据字典和动态性能视图
数据字典:有数据库的系统信息;动态视图:记载了历程启动后的相关信息
数据字典记录了数据库的系统信息,它是只读表和视图的集合,数据字典的所有者为sys用户。
用户只能在数据字典上执行查询操作(select语句),而其维护和修改是由系统自动完成的。
这里我们谈谈数据字典的组成:数据字典包括数据字典基表和数据字典视图,其中基表存储数据库的基本信息,普通用户不能直接访问数据字典的基表。数据字典视图是基于数据字典基表所建立的视图,普通用户可以通过查询数据字典视图取得系统信息。数据字典视图主要包括user_xxx,all_xxx,dba_xxx三种类型。
user_tables;n
用于显示当前用户所拥有的所有表,它只返回用户所对应方案的所有表
比如:select table_name from user_tables;
all_tables;n
用于显示当前用户可以访问的所有表,它不仅会返回当前用户方案的所有表,还会返回当前用户可以访问的其它方案的表:
比如:select table_name from all_tables;
dba_tables;n
它会显示所有方案拥有的数据库表。但是查询这种数据库字典视图,要求用户必须是dba角色或是有select any table系统权限。
例如:当用system用户查询数据字典视图dba_tables时,会返回system,sys,scott...方案所对应的数据库表。
1)用户名,权限,角色
dba_users可以显示所有数据库用户的详细信息;
dba_sys_privs,可以显示用户所具有的系统权限;
ba_tab_privs,可以显示用户具有的对象权限;
dba_col_privs可以显示用户具有的列权限;
dba_role_privs可以显示用户所具有的角色。
2)表空间
建立数据表空间n
在建立数据库后,为便于管理表,最好建立自己的表空间
create tablespace data01 datafile 'd: estdada01.dbf' size 20m uniform size 128k;
说明:执行完上述命令后,会建立名称为data01的表空间,并为该表空间建立名称为data01.dbf的数据文件,区的大小为128k
n 使用数据表空间 :create table mypart(deptno number(4), dname varchar2(14), loc varchar2(13)) tablespace data01;
使表空间脱机 :alter tablespace 表空间名 offline;
使表空间联机 :alter tablespace 表空间名 online;
只读表空间
当建立表空间时,表空间可以读写,如果不希望在该表空间上执行update,delete,insert操作,那么可以将表空间修改为只读
alter tablespace 表空间名 read only;
(修改为可写是 alter tablespace 表空间名 read write;)
删除表空间:drop tablespace ‘表空间’ including contents and datafiles;
说明:including contents表示删除表空间时,删除该空间的所有数据库对象,而datafiles表示将数据库文件也删除。
表空间扩展
1. 增加数据文件
SQL> alter tablespace sp01 add datafile ‘d: estsp01.dbf’ size 20m;
2. 增加数据文件的大小
SQL> alter tablespace 表空间名 ‘d: estsp01.dbf’ resize 20m;
这里需要注意的是数据文件的大小不要超过500m。
3. 设置文件的自动增长。
SQL> alter tablespace 表空间名 ‘d: estsp01.dbf’ autoextend on next 10m maxsize 500m;
7.约束
在oracle中,约束包括:not null、 unique, primary key, foreign key,和check五种。
例子:
商店售货系统表设计案例n
现有一个商店的数据库,记录客户及其购物情况,由下面三个表组成:商品goods(商品号goodsId,商品名 goodsName,单价 unitprice,商品类别category,供应商provider);
客户customer(客户号customerId,姓名name,住在address,电邮email,性别sex,身份证cardId);
购买purchase(客户号customerId,商品号goodsId,购买数量nums);
请用SQL语言完成下列功能:
1. 建表,在定义中要求声明:
(1). 每个表的主外键;
(2). 客户的姓名不能为空值;
(3). 单价必须大于0,购买数量必须在1到30之间;
(4). 电邮不能够重复;
(5). 客户的性别必须是 男 或者 女,默认是男;
SQL> create table goods(goodsId char(8) primary key, --主键 goodsName varchar2(30), unitprice number(10,2) check(unitprice>0), category varchar2(8), provider varchar2(30) ); SQL> create table customer( customerId char(8) primary key, --主键 name varchar2(50) not null, --不为空 address varchar2(50), email varchar2(50) unique, sex char(2) default '男' check(sex in ('男','女')), -- 一个char能存半个汉字,两位char能存一个汉字 cardId char(18) ); SQL> create table purchase( customerId char(8) references customer(customerId), goodsId char(8) references goods(goodsId), nums number(10) check (nums between 1 and 30) ); 表是默认建在SYSTEM表空间的
例子2
如果在建表时忘记建立必要的约束,则可以在建表后使用alter table命令为表增加约束。但是要注意:增加not null约束时,需要使用modify选项,而增加其它四种约束使用add选项。
1. 增加商品名也不能为空 SQL> alter table goods modify goodsName not null; 2. 增加身份证也不能重复 SQL> alter table customer add constraint xxxxxx unique(cardId); 3. 增加客户的住址只能是’海淀’,’朝阳’,’东城’,’西城’,’通州’,’崇文’,’昌平’; SQL> alter table customer add constraint yyyyyy check (address in (’海淀’,’朝阳’,’东城’,’西城’,’通州’,’崇文’,’昌平’));
1)删除约束
当不再需要某个约束时,可以删除。
alter table 表名 drop constraint 约束名称;
特别说明一下:
在删除主键约束的时候,可能有错误,比如:
alter table 表名 drop primary key;
这是因为如果在两张表存在主从关系,那么在删除主表的主键约束时,必须带上cascade选项 如像:
alter table 表名 drop primary key cascade;
2)显示约束
A.显示约束信息
通过查询数据字典视图user_constraints,可以显示当前用户所有的约束的信息。
select constraint_name, constraint_type, status, validated from user_constraints where table_name = '表名';
B.显示约束列
通过查询数据字典视图user_cons_columns,可以显示约束所对应的表列信息。
select column_name, position from user_cons_columns where constraint_name = '约束名';
C.当然也有更容易的方法,直接用pl/sql developer查看即可
3)表级定义 列级定义
A: 列级定义n
列级定义是在定义列的同时定义约束。
如果在department表定义主键约束
create table department4(dept_id number(12) constraint pk_department primary key,
name varchar2(12), loc varchar2(12));
B: 表级定义n
表级定义是指在定义了所有列后,再定义约束。这里需要注意:
not null约束只能在列级上定义。
以在建立employee2表时定义主键约束和外键约束为例:
create table employee2(emp_id number(4), name varchar2(15),
dept_id number(2), constraint pk_employee primary key (emp_id),
constraint fk_department foreign key (dept_id) references department4(dept_id));
------------------------------------休息一下----------------------------------------------------
11.pl/sql分类 -- 过程,函数,包,触发器
标识规范:变量:v_;常量:c_;游标:_cursor;例外:e_开头的
1)Pl/sal编程就是块编程
pl/sql块由三个部分构成:定义部分,执行部分,例外处理部分。
如下所示:
declare /*定义部分——定义常量、变量、游标、例外、复杂数据类型*/ begin /*执行部分——要执行的pl/sql语句和sql语句*/ exception /*例外处理部分——处理运行的各种错误*/ end; 定义部分是从declare开始的,该部分是可选的; 执行部分是从begin开始的,该部分是必须的; 例外处理部分是从exception开始的,该部分是可选的。 可以和java编程结构做一个简单的比较。
例子:
declare --定义变量 v_ename varchar2(5); v_sal number(7,2); begin --执行部分 select ename,sal into v_ename,v_sal from emp where empno=&aa; --在控制台显示用户名 dbms_output.put_line('用户名是:'||v_ename||' 工资:'||v_sal); --异常处理 exception when no_data_found then dbms_output.put_line('朋友,你的编号输入有误!'); end; /
2)过程
例子:
创建: create or replace procedure sp_pro1 is begin--执行部分 insert into mytest values('韩顺平','m1234'); end; 调用 A:exec 过程名(参数值1,参数值2...); B:call 过程名(参数值1,参数值2...); C:java中如何调用 例子: import java.sql.*; public class TestOraclePro{ public static void main(String[] args){ try{ //1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); //2.得到连接 Connection ct = DriverManager.getConnection("jdbc:oracle:thin@127.0.0.1:1521:MYORA1","scott","m123"); //3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call sp_pro3(?,?)}"); //4.给?赋值 cs.setString(1,"SMITH"); cs.setInt(2,10); //5.执行 cs.execute(); //关闭 cs.close(); ct.close(); } catch(Exception e){ e.printStackTrace(); } } }
3)函数
函数用于返回特定的数据,当建立函数时,在函数头部必须包含return子句
--输入雇员的姓名,返回该雇员的年薪 create function annual_incomec(name varchar2) return number is annual_salazy number(7,2); begin --执行部分 select sal*12+nvl(comm, 0) into annual_salazy from emp where ename=name; return annual_salazy; end; / 如果函数创建有问题可以show error来查问题 Pl/sql中调用函数 SQL> var income number SQL> call annual_incomec('scott') into: income; SQL> print income 同样我们可以在java程序中调用该函数 select annual_income('SCOTT') from dual; 这样可以通过rs.getInt(l)得到返回的结果。
4)包
逻辑上组合过程和函数,分为包规范包体两部分
创建包: a.--创建一个包sp_package b--声明该包有一个过程update_sal c--声明该包有一个函数annual_income Sql代码: create package sp_package is procedure update_sal(name varchar2, newsal number); function annual_income(name varchar2) return number; end; 包的规范只包含了过程和函数的说明,但是没有过程和函数的实现代码。包体用于实现包规范中的过程和函数。 创建包体的Sql代码 create or replace package body sp_package is procedure update_sal(name varchar2, newsal number) is begin update emp set sal = newsal where ename = name; end; function annual_income(name varchar2) return number is annual_salary number; begin select sal * 12 + nvl(comm, 0) into annual_salary from emp where ename = name; return annual_salary; end; end; / 调用包:都是调用包的成员call sp_package.update_sal('SCOTT', 1500);
5)触发器
常用的触发事件包括insert,update,delete语句,而触发操作实际就是一个pl/sql块。可以使用create trigger来建立触发器
12.定义并使用变量,复合类型
标量类型,复合类型,参照类型,lob
标识规范:变量:v_;常量:c_;游标:_cursor;例外:e_开头的
1)标量类型(scalar)
定义:name datatype [not null] [:=| default expr]
其中datatype还可以用数据库表中列类型表示:emp.ename%type
例子:
例子:定义一个布尔变量,不能为空,初始值为false v_valid boolean not null default false; SQL例子: declare c_tax_rate number(3,2):=0.03; --用户名 v_ename emp.ename%type; v_sal number(7,2); v_tax_sal number(7,2); begin --执行 select ename,sal into v_ename,v_sal from emp where empno=&no; --计算所得税 v_tax_sal := v_sal*c_tax_rate; --输出 dbms_output.put_line('姓名是:'||v_ename||'工资:'||v_sal||' 交税:'||v_tax_sal); end; /
2)复合类型(composite)
A:类似高级语言中的结构体,调用方式(类型名.成员名)
例子:
type emp_record_type is record( name emp.ename%type, salary emp.sal%type, title emp.job%type); --定义了一个sp_record变量,这个变量的类型是emp_record_type sp_record emp_record_type;
B:类似高级语言中的数组,但是下标没有限制可以为负数
C:嵌套表(nested table)
D:变长数组
3)参照类型(reference)
游标变量(ref cursor)和对象类型变量(ref obj_type)
A:游标变量 例子: 1.请使用pl/sql编写一个块,可以输入部门号,并显示该部门所有员工姓名和他的工资。 2.在1的基础上,如果某个员工的工资低于200元,就添加100元。 B:对象类型变量 1.SQL declare --定义游标sp_emp_cursor type sp_emp_cursor is ref cursor; --定义一个游标变量 test_cursor sp_emp_cursor; --定义变量 v_ename emp.ename%type; v_sal emp.sal%type; begin --执行 --把test_cursor和一个select结合 open test_cursor for select ename,sal from emp where deptno=&no; --循环取出 loop fetch test_cursor into v_ename,v_sal; --判断是否test_cursor为空 exit when test_cursor%notfound; dbms_output.put_line('名字:'||v_ename||' 工资:'||v_sal); end loop; end; /
4)lob(large object)
13.pl/sql的进阶--控制结构(分支,循环,控制)
1)三种分支语句:if—then,if – then – else,if – then – elsif – then
例子:编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该员工工资增加10%
create or replace procedure sp_pro6(spName varchar2) is --定义 v_sal emp.sal%type; begin --执行 select sal into v_sal from emp where ename=spName; --判断 if v_sal<2000 then update emp set sal=sal+sal*10% where ename=spName; end if; end; /
2)循环
A:loop循环语句以loop开头,以end loop结尾
例子:案例:现有一张表users,表结构如下:
用户id | 用户名
|
请编写一个过程,可以输入用户名,并循环添加10个用户到users表中,用户编号从1开始增加。
create or replace procedure sp_pro6(spName varchar2) is --定义 :=表示赋值 v_num number:=1; begin loop insert into users values(v_num,spName); --判断是否要退出循环 exit when v_num=10; --自增 v_num:=v_num+1; end loop; end; /
B:while
循环以while...loop开始,以end loop结束
例子:
案例:现有一张表users,表结构如下:
用户id 用户名
问题:请编写一个过程,可以输入用户名,并循环添加10个用户到users表中,用户编号从11开始增加。
SQL: create or replace procedure sp_pro6(spName varchar2) is --定义 :=表示赋值 v_num number:=11; begin while v_num<=20 loop --执行 insert into users values(v_num,spName); v_num:=v_num+1; end loop; end; /
C:for循环
例子: begin for i in reverse 1..10 loop insert into users values (i, 'shunping'); end loop; end;
end;
D:顺序控制语句goto,null
Goto:
基本语法如下 goto lable,其中lable是已经定义好的标号名
例子: declare i int := 1; begin loop dbms_output.put_line('输出i=' || i); if i = 1{} then goto end_loop; end if; i := i + 1; end loop; <<end_loop>> dbms_output.put_line('循环结束'); end;
Null:类似于高级语句中的continue:直接将控制传递到下一条语句
例子: declare v_sal emp.sal%type; v_ename emp.ename%type; begin select ename, sal into v_ename, v_sal from emp where empno = &no; if v_sal < 3000 then update emp set comm = sal * 0.1 where ename = v_ename; else null; end if; end;
------------------------------------休息一下----------------------------------------------------
14.PL/SQL分页
一步一步来
1)有返回值的存储过程完整例子(列表[结果集])
例子:编写一个过程,输入部门号,返回该部门所有雇员信息。
分析: 由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用pagkage了。所以要分两部分: A:创建一个包,在这个包中定义类型test_cursor游标 create or replace package testpackage as TYPE test_cursor is ref cursor; end testpackage; B:创建存储过程 create or replace procedure sp_pro9(spNo in number,p_cursor out testpackage.test_cursor) is begin open p_cursor for select * from emp where deptno = spNo; end sp_pro9; 最后在java中调用这个存储过程 try{ //1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); //2.得到连接 Connection ct = DriverManager.getConnection("jdbc:oracle:thin@127.0.0.1:1521:MYORA1","scott","m123"); //看看如何调用有返回值的过程 //3.创建CallableStatement /*CallableStatement cs = ct.prepareCall("{call sp_pro9(?,?)}"); //4.给第?赋值 cs.setInt(1,10); //给第二个?赋值 cs.registerOutParameter(2,oracle.jdbc.OracleTypes.CURSOR); //5.执行 cs.execute(); //得到结果集 ResultSet rs=(ResultSet)cs.getObject(2); while(rs.next()){ System.out.println(rs.getInt(1)+" "+rs.getString(2)); } } catch(Exception e){ e.printStackTrace(); } finally{ //6.关闭各个打开的资源 cs.close(); ct.close(); }
2)编写分页过程
Sql代码: select * from (select t1.*, rownum rn from (select * from emp) t1 where rownum<=10) where rn>=6;
create or replace package testpackage as TYPE test_cursor is ref cursor; end testpackage;
开始编写分页的过程
create or replace package testpackage as TYPE test_cursor is ref cursor; end testpackage; --开始编写分页的过程 create or replace procedure fenye (tableName in varchar2, Pagesize in number,--一页显示记录数 pageNow in number, myrows out number,--总记录数 myPageCount out number,--总页数 p_cursor out testpackage.test_cursor--返回的记录集 ) is --定义部分 --定义sql语句 字符串 v_sql varchar2(1000); --定义两个整数 v_begin number:=(pageNow-1)*Pagesize+1; v_end number:=pageNow*Pagesize; begin --执行部分 v_sql:='select * from (select t1.*, rownum rn from (select * from '||tableName||') t1 where rownum<='||v_end||') where rn>='||v_begin; --把游标和sql关联 open p_cursor for v_sql; --计算myrows和myPageCount --组织一个sql语句 v_sql:='select count(*) from '||tableName; --执行sql,并把返回的值,赋给myrows; execute inmediate v_sql into myrows; --计算myPageCount --if myrows%Pagesize=0 then这样写是错的 if mod(myrows,Pagesize)=0 then myPageCount:=myrows/Pagesize; else myPageCount:=myrows/Pagesize+1 end if; --关闭游标 close p_cursor; end; /
java测试
try{ //1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); //2.得到连接 Connection ct = DriverManager.getConnection("jdbc:oracle:thin@127.0.0.1:1521:MYORA1","scott","m123"); //3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call fenye(?,?,?,?,?,?)}"); //4.给第?赋值 cs.seString(1,"emp"); cs.setInt(2,5); cs.setInt(3,2); //注册总记录数 cs.registerOutParameter(4,oracle.jdbc.OracleTypes.INTEGER); //注册总页数 cs.registerOutParameter(5,oracle.jdbc.OracleTypes.INTEGER); //注册返回的结果集 cs.registerOutParameter(6,oracle.jdbc.OracleTypes.CURSOR); //5.执行 cs.execute(); //取出总记录数 /这里要注意,getInt(4)中4,是由该参数的位置决定的 int rowNum=cs.getInt(4); int pageCount = cs.getInt(5); ResultSet rs=(ResultSet)cs.getObject(6); //显示一下,看看对不对 System.out.println("rowNum="+rowNum); System.out.println("总页数="+pageCount); while(rs.next()){ System.out.println("编号:"+rs.getInt(1)+" 名字:"+rs.getString(2)+" 工资:"+rs.getFloat(6)); } } catch(Exception e){ e.printStackTrace(); } finally{ //6.关闭各个打开的资源 cs.close(); ct.close(); } 运行,控制台输出: rowNum=19 总页数:4 编号:7369 名字:SMITH 工资:2850.0 编号:7499 名字:ALLEN 工资:2450.0 编号:7521 名字:WARD 工资:1562.0 编号:7566 名字:JONES 工资:7200.0 编号:7654 名字:MARTIN 工资:1500.0
15.例外处理
例外分为预定义例外,非预定义例外和自定义例外三种
预定义例外用于处理常见的oracle错误
非预定义例外用于处理预定义例外不能处理的例外
自定义例外用于处理与oracle错误无关的其它情况
1)预定义例外
预定义例外有20多个预定义例外
1.case_not_found:在when子句中没有包含必须的条件分支发生例外
2.cursor_already_open:重新打开已经打开的游标时,会隐含的触发
3.dup_val_on_index:在唯一索引所对应的列上插入重复的值
4.invalid_cursor:当试图在不合法的游标上执行操作时
5.invalid_number:输入的数据有误时
6. no_data_found :当执行select into 没有返回行
7.too_many_rows:当执行select into语句时,返回超过了一行
8.zero_divide:执行2/0语句时,则会触发该例外
9.value_error:执行赋值操作时,变量的长度不足以容纳实际数据
10.其他预定义例外是出现在pl/sql中的而不是oracle中发生的
Login_denied:用户非法登录
Not_logged_on:没有用户登录执行dml操作
Storage_error:超过内存空间
Timeout_on_resource:等待资源超时
--定义 V_name emp.ename%type; Begin -- Select ename into v_ename from emp where empno=%gno; Dbms_output.put_line(‘名字:’||v_ename) Exception When no_data_found then Dbms_output.putline(‘编号没有!’); / 执行输入一个不存在的编号,回显:编号没有!
2)非预定义例外
比如在pl/sql块中执行dml语句时,违反了约束规定等等很少有这样的例外
3)自定义例外
自定义例外与oracle错误没有任何关联,它是由开发人员为特定情况所定义的例外
例子:请编写一个pl/sql块,接收一个雇员的编号,并给该雇员工资增加1000元,如果该雇员不存在,请提示。
自定义例外
create or replace procedure ex_test(spNo number) is --定义一个例外 myex exception; begin --更新用户sal update emp set sal=sal+1000 where empno=spNo; --sql%notfound这是表示没有update --raise myex;触发myex if sql%notfound then raise myex; end if; exception when myex then dbms_output.put_line('没有更新任何用户'); end; / --自定义例外create or replace procedure ex_test(spNo number)is 测试 SQL> exec ex_test(56);
没有更新任何用户
16 oracle的视图
1)视图与表的区别n
表需要占用磁盘空间,视图不需要
视图不能添加索引(所以查询速度略微慢点)
使用视图可以简化,复杂查询
比如:学生选课系统
视图的使用利于提高安全性
比如:不同用户查看不同视图
2)创建修改视图
创建视图:create view 视图名 as select 语句[with read only]
创建或修改视图:create or replace view 视图名 as select 语句[with read only]
删除视图:drop view 视图名
当表结构复杂的时候我们可以用视图!
例子:为简化操作,用一个视图解决 显示雇员编号,姓名和部门名称
Sql代码
create view myview2 as select emp.empno,emp.ename,dept.dname from emp,dept where emp.deptno=dept.deptno;
create view myview2 as select emp.empno,emp.ename,dept.dname from emp,dept where emp.deptno=dept.deptno;
视图之间也可以做联合查
17.查询优化
1)尽量用EXISTS代替IN操作
2)用NOT EXISTS 或者外链接代替NOT IN
因为NOT IN 不能应用表索引
3)尽量不用“<>”或者!=
QTH1`7Z$)T_8}K64Z)9L{_L