Oracle数据库

数据库对象和多表查询


■ 高级查询 多表查询

如果查询两张表,并且没有定义连接条件,那么查询的结果集是两表相乘的结果,这样的情况称之为笛卡尔乘积
笛卡尔乘积会产生很多行结果。比如,第一张表的行数为50行,第二张表为100行,那么笛卡尔乘积产生的结果为5000行。因为第一张表的每条记录会对应第二张表的100条记录,即50*100=5000
例如
SELECT e.empno, e.ename,d.deptno
FROM emp e,dept d;
此时雇员表emp有14条记录,dept有4条记录,查询后有56条记录,所有为了消除笛卡尔乘积,我们在做多表查询的时候,要加上连接条件。

■ 连接条件和连接类型
常见的有两类连接条件
等值连接:一般使用等于号(=)操作符
非等值连接:使用除了等于以外的运算符,一般使用<>、>、<、<=、>=、LIKE、IN、BETWEEN等运算符
常见的三种连接类型
内连接
内连接返回的行只有满足连接条件才会返回。如果连接条件的列中有NULL值,那么该行则不会返回
Inner join
外连接
外连接返回的行满足连接条件,也包括在连接条件的列包含空值的行
Left outer join,right outer join
自连接
连接的表为同一张表

自连接:
例如要求查询出员工及他的上级。
SELECT e.ename AS 员工,b.ename AS 领导 FROM emp e LEFT JOIN emp b ON e.mgr=b.empno ORDER BY b.ename;
外连接:可以查询出即使列中包含NULL值的数据也可以查询出来
外部连接运算符 (+) 。在使用(+)的字段,可能包含NULL值
例如:查询出部门的员工的情况(列出所有部门)
SELECT e.ename, d.dname FROM emp e ,dept d
WHERE d.deptno = e.deptno(+) ORDER BY d.dname;



■ 左外连接右外连接




现有有以下查询语句


SELECT...

FROMtable1, table2

...


其中 table1.column1 和 table2.column2 为连接条件


假设 1 , table1 表包含了某行的 column1 列的值为 NULL


左外连接的 WHERE 条件写作

WHEREtable1.column1 = table2.column2 (+);
 
假设 
 2 
 , 
 table2 
 表包含了某行的 
 column2 
 列的值为 
 NULL
 
右外连接的 
 WHERE 
 条件写作
 
WHEREtable1.column1 (+) = table2.column2;

左外连接:


例如:查询出部门的员工的情况(显示所有部门)


SELECTe.ename, d.dname   FROM emp e ,dept d
WHEREd.deptno = e.deptno(+)   ORDER BY d.dname;


右外连接:


例如:查询出所有的员工的部门情况(显示了所有员工)


SELECTe.ename, d.dname   FROM emp e ,dept d
WHEREd.deptno(+) =e.deptno  ORDER BY d.dname;

只能一侧使用外连接运算符(+),否则将提示错误


不可以在使用外连接的情况下同时使用IN运算符


(+)是Oracle所独有的,其他数据库不适用



■ 

多列子查询


例如,通过多列子查询查询出每个部门员工的最低工资,然后再查询出满足这些条件的员工信息

SELECT empno, sal FROM EMP 

WHERE (deptno, sal) 
 IN (SELECT deptno, MIN(sal) FROM emp GROUP BY deptno);




■ 

嵌套子查询

SQL提供嵌套子查询机制,即在一个查询中嵌套另一个 select - from - where查询语句.子查询中嵌套子查询,最多可以嵌套255层
例如:查询工资高于SMITH工资的所有员工
select * from emp where sal>(select sal from emp where ename='SMITH')
可以使用all关键词
例如:查询工资高于所有部门的平均工资的员工
select * from emp where sal>all(select avg(sal) from emp group by deptno)
--查询工资高于任何部门的平均工资的员工

■ UPDATE和DELETE使用子查询

可以将子查询用在UPDATE和DELETE语句中


可以在UPDATE语句中,使用单行子查询返回的结果给列赋值


例如,将empno为4的员工工资进行更新


UPDATE emp 

 SET sal = 

 (SELECT AVG(hisal) 

 FROM salgrade) 

WHERE employee_id = 4;



可以利用子查询返回的结果给 DELETE 语句的 WHERE 条件进行 DELETE 操作


例如,删除工资高于平均工资的员工


DELETEFROM emp
WHEREsal >
  (SELECT AVG(hisal)
   FROM salgrade);



■ 行的标识符


在Oracle数据库中的每一行都有一个唯一的行标识符,它用于Oracle数据库内部存储行的物理位置。


例如,查看EMP表的信息

SELECT ROWID, empno FROM EMP; 

 需要注意的是ROWID并不出现在表的定义中 

 例如,可以使用DESCRIBE命令查看EMP表的信息 

 DESCRIBE EMP




ROWNUM可以返回结果集的行号,查询返回的第一行编号为1,第二行编号为2,依此类推
例如,查询EMP表的相关信息

SELECT ROWNUM, empno, ename, deptno FROM EMP;
 可以将ROWNUM使用在WHERE子句中
 SELECT ROWNUM, empno, ename, deptno 
 FROM EMP where empno=7369;ROWNUM行号并不是永久固定的,是每次动态重新生成的。此时如果想查找第6-10条记录,用between…and…的方式是没有数据的。这个时候要想真正的实现这种查询思路:先查询前10条记录,之后再显示5条记录,要依靠子查询完成。
 SELECT * FROM (
SELECT ROWNUM M,EMPNO,ENAME,JOB,HIREDATE,SAL FROM EMP
WHERE ROWNUM<=10
 ) TEMP WHERE TEMP.M>5;
 或写成
 SELECT * FROM (
SELECT ROWNUM M,EMPNO,ENAME,JOB,HIREDATE,SAL FROM EMP
 ) WHERE M>5 AND M<=10;


按照这种方式,只需要替换掉最大最小值就可以实现分页的思想

■ 数据库对象

序列Sequence

■ 创建序列


可以使用 CREATESEQUENCE 语句来创建序列,语法如下:

CREATE SEQUENCE sequence_name
[START WITH start_num]
[INCREMENT BY increment_num]
[ { MAXVALUE maximum_num |NOMAXVALUE } ]
[ { MINVALUE minimum_num |NOMINVALUE } ]
[ { CYCLE | NOCYCLE } ]
[ { CACHE cache_num |NOCACHE } ]
[ { ORDER | NOORDER } ];

说明


sequence_name :序列的名称


start_num :序列开始的第一个整数,默认从 1 开始


increment_num :每次增长的数值


maximum_num :序列能够增长到的最大值


NOMAXVALUE :对于序列最大值,不指定最大值,
对于升序最大值是 10 27 ,对于降序是 -1





可以使用CREATE SEQUENCE语句来创建序列,语法如下:


CREATE SEQUENCE sequence_name 

 [START WITH start_num] 

 [INCREMENT BY increment_num] 

 [ { MAXVALUE maximum_num | NOMAXVALUE } ] 

 [ { MINVALUE minimum_num | NOMINVALUE } ] 

 [ { CYCLE | NOCYCLE } ] 

 [ { CACHE cache_num | NOCACHE } ] 

 [ { ORDER | NOORDER } ];


说明


minimum_num:序列中的最小值,该值必须比start_num小,比maximum_num也要小


NOMINVALUE:对于序列最小值,不指定最小值,对于升序最小值是1,对于降序最小值是-1026


CYCLE:对于序列的值达到最大值或最小值时的处理方法。如果是升序达到最大值了,那么下一个生成的值将是最小值;如果是降序达到最小值时,那么下一个值将是最大值。


NOCYCLE:当序列的值达到最大或最小值时, 序列将不再产生任何数值。默认是NOCYCLE。



可以使用CREATE SEQUENCE语句来创建序列,语法如下:


CREATE SEQUENCE sequence_name 

 [START WITH start_num] 

 [INCREMENT BY increment_num] 

 [ { MAXVALUE maximum_num | NOMAXVALUE } ] 

 [ { MINVALUE minimum_num | NOMINVALUE } ] 

 [ { CYCLE | NOCYCLE } ] 

 [ { CACHE cache_num | NOCACHE } ] 

 [ { ORDER | NOORDER } ];


说明


cache_num:保存在内存中缓存整数的数量,默认缓存的数量是20,最小的缓存数是2,最大的缓存数是CEIL(maximum_num -minimum_num)/ABS(increment_num)


NOCACHE:表示没有缓存数字


ORDER:保证整数的请求顺序是按照生成顺序得到的。


NOORDER:不保证整数的请求顺序是按照生成顺序得到的。



可以通过查看 user_sequences 视图了解序列的信息


查看序列信息


SELECT* FROM user_sequences
ORDERBY sequence_name;



■ 

使用序列


创建序列之后,序列可以产生一系列的数值。可以通过currval和nextval来访问它们来获取当前值和下一个值。
例如
SELECT s_test.nextval
FROM dual;


在s_test序列中的第一个值是1,一旦序列被初始化之后,就可以从序列中获取当前值
SELECT s_test.currval
FROM dual;


s_test2序列的初始值是10,每次增加5,最大值为20
执行3次
SELECT s_test2.nextval
FROM dual;
说明
由于设置了循环,当达到最大值20时,
又会重新从10开始进行循环


序列经常用于填充整型的主键列


例如,创建order_status2表

CREATE TABLE order_status2 ( 

 id INTEGER CONSTRAINT order_status2_pk PRIMARY KEY, 

 status VARCHAR2(10), 

 last_modified DATE DEFAULT SYSDATE 

 ); 

 创建序列 

 CREATE SEQUENCE s_order_status2 NOCACHE;



插入数据



INSERT INTO order_status2(id, status, last_modified) 

 VALUES (s_order_status2.nextval, 'PLACED', '01-1月-2006'); 

 INSERT INTO order_status2 (id, status, last_modified) 

 VALUES (s_order_status2.nextval, 'PENDING', '01-2月-2006');

查询插入结果


SELECT * 

 FROM order_status2;



注意


当使用序列填充主键列时,通常应使用NOCACHE避免序列产生的数值发生遗漏(数值产生遗漏是因为缓存数值时,数据库被关闭)。然而,使用NOCACHE虽然会降低性能。但是如果不介意主键值产生遗漏,可以再考虑使用CACHE。



修改序列


修改序列可以使用ALTER SEQUENCE语句进行修改。修改有一些限制如下:


初始值不能修改


序列的最小值不能大于当前序列的值


序列的最大值不能小于当前序列的值


例如


修改s_test序列的增长值为2


ALTER SEQUENCE s_test 

 INCREMENT BY 2;


查看序列情况


SELECT s_test.currval 

 FROM dual; 

 SELECT s_test.nextval 

 FROM dual;


删除序列

删除序列使用DROP SEQUENCE语句
例如,删除s_test3序列
DROP SEQUENCE s_test3;

■ 索引

索引是与表关联的可选结构。可以创建索引以加快对表执行SQL语句的速度。就像书的索引可以帮助我们更快速的查找信息一样,Oracle中的索引也提供了一种更快地访问表数据的途径。

应该建索引列的特点:
1)在经常需要搜索的列上,可以加快搜索的速度;
2)在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
3)在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
5)在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
6)在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

不应该建索引列的特点:
第一,对于那些在查询中很少使用或者参考的列不应该创建索引。
第二,对于那些只有很少数据值的列也不应该增加索引。
第三,对于那些定义为blob数据类型的列不应该增加索引。

索引的特点

优点:
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。


缺点:
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。


索引学习部分内容包括
创建B-Tree索引(默认类型)
创建函数索引
查看索引信息
修改索引
删除索引
创建位图索引


创建B-Tree索引

可以使用CREATE INDEX创建B-Tree索引,语法如下


CREATE [UNIQUE] INDEX index_name ON 

 table_name(column_name[, column_name ...]) 

 TABLESPACE tab_space;


说明


UNIQUE:意味着索引列中的值必须是唯一的


index_name:索引的名称


table_name:需要加索引的表名


column_name:需要加索引的列。可以在多列上加索引(这样的索引称为复合索引)


tab_space:存储索引的表空间。如果不指定的话,那么索引将存储在用户默认的表空间下


出于性能原因,可以将索引存储的表空间与实际表所存储的表空间分开。管理员应该为表和索引创建不同的表空间。



B树索引在Oracle中是一个通用索引。

在创建索引时它就是默认的索引类型。

B树索引可以是一个列的(简单)索引,也可以是组合/复合(多个列)的索引。B树索引最多可以包括32列。

B-tree 特点: 适合与大量的增、删、改(OLTP) 不能用包含OR操作符的查询; 适合高基数的列(唯一值多) 典型的树状结构; 每个结点都是数据块; 大多都是物理上一层、两层或三层不定,逻辑上三层; 叶子块数据是排序的,从左向右递增; 在分支块和根块中放的是索引的范围;


查看索引


可以通过查看user_indexes视图,了解索引的信息
例如

SELECT index_name, table_name, uniqueness, status 
 
 FROM user_indexes 
 
 WHERE table_name IN ('CUSTOMERS', 'EMPLOYEES') 
 
 ORDER BY index_name;

说明
以上语句查询出customers表和emp表的索引信息
customers_pk索引是在创建表时设置customer_id列为主键时数据库自动创建的
相关列的说明
INDEX_NAME:索引名
TABLE_NAME:索引所属表的名称
UNIQUENES:是否是唯一索引,UNIQUE或NONUNIQUE
STATUS:当前索引是否有效,VALID或INVALID


可以通过user_ind_columns视图查看某列的索引信息

例如 
 
 SELECT index_name, table_name, column_name 
 
 FROM user_ind_columns 
 
 WHERE table_name IN ('CUSTOMERS', 'EMP') 
 
 ORDER BY index_name;

可以使用ALTER INDEX语句修改索引
例如, 修改i_customers_phone索引名为i_customers_phone_number索引

ALTER INDEX i_customers_phone 
  
 RENAME TO i_customers_phone_number


可以使用DROP INDEX删除索引


例如,删除i_customers_phone_number索引



DROP INDEX i_customers_phone_number;

■ 位图索引


位图索引通常用于数据仓库
数据仓库里的数据一般是用来进行大量的查询,而一般不是用来被大量的并发事务进行修改的。数据仓库通常被用于商业组织的智能分析,像监控销售趋势,等等
可以创建位图索引的列,一般会被用来进行大量的查询,但是列的基数确不大。比如:
1、2、3、4、5、6
N、S、E、W
“Order placed”, “Order shipped”
位图索引比较有代表性的应用是表中含有大量数据,而且不经常修改。如果表中列的不同值的行数数目小于1%,或如果列中的值是重复的100倍以上,则该列是一个位图索引的候选键。
例如,一张表中有100万行数据,某列的不同值达到10000甚至更少,那么该列可以用来创建位图索引。同样,该列应该尽可能的少修改,而且该列应该常用于WHERE子句的查询。

在order_status表的status列上创建位图索引


修改表名


RENAME emp TO emp2;



创建位图索引


CREATE BITMAP INDEX i_emp_ename 

 ON emp(ename);




■ 

总结


在以下情况,应该考虑创建索引:


列包含较大范围的值


列包含大量空值


在WHERE子句或连接条件中频繁使用一个或多个列


表相当大,但是预计的多数查询检索的行占总行数比例较小,如百分之十以下



在以下情况,通常不值得创建索引:
表比较小
在查询中不经常使用列作为条件
预计多数查询检索的行要超过表中总数行数的一定比例,如百分之十
表更新比较频繁
被索引的列将作为表达式的一部分进行引用


l B 树


    默认的索引类型,平衡树索引,适用于高基数(不同值的程度高)的列。除非有特殊原因需要使用不同的索引类型或功能,否则用正常的B树索引即可.


l 唯一索引


     B树索引的一种形式,用于强制执行列值的唯一性.经常与主键和唯一键约束一起使用,但也可以独立于约束而创建


l 位图索引


     对于包含低基数列以及在SQL语句的WHERE子句中使用许多AND或OR运算符的数据仓库环境,非常适合使用这种索引。


l 函数索引


     适用于应用了SQL函数的列。可与B树索引类型或位图索引类型结合使用



■ 

视图



视图是基于已知的一张或多张表的查询
查询视图与查询普通表的查询方式是一样的
一些视图是可以对其进行DML操作,从而影响其基表的操作
视图实际并不存储数据,数据存储实际是存储在表中
之前已经使用过很多视图查看信息。
比如:user_tables,user_sequences 以及user_indexes都是视图


将复杂的查询用视图来实现,然后给用户授权访问
这样可以隐藏复杂的业务逻辑
可以禁止用户直接访问基表,授予用户访问视图的权限
可以允许视图来访问基表中的某些行
可以对最终用户隐藏某些行

( 
--创建视图
 create view v1 as select * from p3;
 --查询视图 
 select * from v1;
 --创建视图
 create view v2 as
 select p.name as pname,c.name as cname from 
 person p inner join car c on p.id=c.pid;
 --查询
 select * from v2;)

数据准备

产品类型表
 CREATE TABLE product_types (
 product_type_id INTEGER CONSTRAINT product_types_pk PRIMARY KEY,
 name VARCHAR2(10) NOT NULL
 );
 产品信息表
 CREATE TABLE products (
 product_id INTEGER CONSTRAINT products_pk PRIMARY KEY,
 product_type_id INTEGER
 CONSTRAINT products_fk_product_types
 REFERENCES product_types(product_type_id),
 name VARCHAR2(30) NOT NULL,
 description VARCHAR2(50),
 price NUMBER(5, 2)
 );可以使用CREATE VIEW语句创建视图,语法如下
 CREATE [OR REPLACE] [{FORCE | NOFORCE}] VIEW view_name
 [(alias_name[, alias_name ...])] AS subquery
 [WITH {CHECK OPTION | READ ONLY} CONSTRAINT constraint_name];
 说明
 OR REPLACE:表示替换已存在的同名视图
 FORCE:强制创建视图,即使基表不存在也创建
 NOFORCE:基表如果不存在,则不创建视图。默认值
 view_name:视图名称
 alias_name:子查询表达式的别名
 subquery:检索基表数据的子查询
 WITH CHECK OPTION:表示只有被查询搜索到的数据行才可以进行插入、更新、删除操作。默认是不使用该项
 constraint_name:是WITH CHECK OPTION或 WITH READ ONLY选项的约束名
 WITH READ ONLY:只能读取基表的数据行


分类

简单视图
查询语句只涉及到一张基表
复杂视图
一般查询涉及到一张或多张表
使用GROUP BY子句或DISTINCT关键字
包含函数的使用

要创建视图,必须给用户授予创建视图CREATE VIEW的权利。可以使用SYS给用户scott授予该权利。
授权语句如下

GRANT CREATE VIEW TO scott;

创建简单视图

使用scott用户创建名为emp_dept_view视图,用以查找部门编号低于20的员工

CREATE VIEW emp_dept_view AS
 SELECT *
 FROM dept
 WHERE deptno < 20
 创建名为emp_view的视图,用以查找emp表中除salary列的信息
 CREATE VIEW emp_view AS
 SELECT empno,mgr, ename,job
 FROM emp;

使用简单视图


查询emp_dept_view视图的数据
 SELECT deptno, dname, loc
 FROM emp_dept_view;
 查询employees_view视图的数据
 SELECT *
 FROM emp_view;

使用视图插入数据

可以使用DML语句对cheap_products_view视图进行操作(注意只可用于简单视图)
例如,对视图进行插入操作

INSERT INTO emp_dept_view (deptno, dname, loc) 
 VALUES (50, 1, ‘West Union', ‘New York’);
 查询视图
 SELECT deptno, dname, loc
 FROM emp_dept_view
 WHERE deptno = 50;

创建视图使用CHECK OPTION

如果想DML语句在视图上进行安全的操作,那么可以在查询上设置CHECK OPTION。例如,

CREATE VIEW emp_dept_view2 AS
 SELECT *
 FROM dept
 WHERE deptno < 20
 WITH CHECK OPTION CONSTRAINT emp_dept_view2_price;
 说明
 WHERE子句的限定跟emp_dept_view视图一样,只不过加了CHECK OPTION限制。

 (-widthcheck option
desc t2;
createor replace view v3 
  as select * from t2 where age<10
  with check option;
select* from v3;
--因为设置了with check option所以age>=10时将不能写入
insertinto v3 values(33,22);
 

 ) 

通过视图插入一条记录
 INSERT INTO emp_dept_view2 (
 deptno, dname, loc)
 VALUES (25, 'Southern Front', ‘New York’)



说明
由于部门编号是25,通过视图是无法将该记录查出的,视图里面筛选的编号是小于20的,所以插入数据时数据库返回一个错误。

使用READ ONLY创建视图

创建视图的时候使用READ ONLY,那么该将为只读视图
例如,创建emp_dept_view3视图使用READ ONLY

CREATE VIEW emp_dept_view3 AS
 SELECT *
 FROM dept
 WHERE deptno < 15
 WITH READ ONLY CONSTRAINT emp_dept_view3_read_only;
 插入测试
 INSERT INTO emp_dept_view3
 (deptno, dname, loc) 
 VALUES (16, ‘Northern Front’,’New York’);查看视图定义
可以使用DESCRIBE命令查看视图定义
 例如,查看emp_dept_view3视图定义
 DESCRIBE emp_dept_view3
同样,可以通过user_views视图查看视图定义信息
 例如
 SELECT view_name,text_length,text
 FROM user_views;
 说明,视图的相关列
 view_name:视图名
 text_length:创建视图的字符长度
 text:创建视图的文本代码
 read_only:是否为只读视图创建复杂视图
创建名为products_and_types_view的视图,使用完全外连接
 CREATE VIEW emp_and_dept_view AS
 SELECT e.empno, e. ename, pt.dname
 FROM emp e FULL OUTER JOIN dept pt
 USING (deptno)
 ORDER BY deptno;
 查询视图
 SELECT * FROM emp_and_dept_view;创建视图emp_salary_grades_view,使用内连接
 CREATE VIEW emp_salary_grades_view AS
 SELECT e.ename, e.job, e.sal, sg.grade
 FROM emp e INNER JOIN salgrade sg
 ON e.sal BETWEEN sg.losal AND sg.hisal
 ORDER BY sg.grade;
 查询视图
 SELECT * FROM emp_salary_grades_view;创建视图sal_average_view,使用WHERE子句、GROUP BY、HAVING
 CREATE VIEW sal_average_view AS
 SELECT ename, AVG(sal) average_price
 FROM emp
 WHERE sal <3000
 GROUP BY empno
 HAVING AVG(sal) > 2000
 ORDER BY empno;
 查询视图
 SELECT * FROM sal_average_view;




修改视图
可以使用CREATE OR REPLACE VIEW修改或替换视图
替换sal_average_view视图

CREATE OR REPLACE sal_average_view AS
 SELECT empno,AVG(sal) average_price
 FROM emp
 WHERE sal < 3000
 GROUP BY empno
 HAVING AVG(sal) > 2000
 ORDER BY empno;
 可以使用ALTER VIEW去除视图相关约束
 例如,去除emp_dept_view2_sal视图的约束
 ALTER VIEW emp_dept_view2
 DROP CONSTRAINT cheap_products_view2_sal;



可以使用DROP VIEW语句删除视图
例如,删除视图emp_dept_view2
DROP emp_dept_view2

创建和删除同义词

使用同义词访问相同的对象:
方便访问其它用户的对象
缩短对象名字的长度

CREATE [PUBLIC] SYNONYM synonym
 FOR object;同义词
为视图DEPT_SUM_VU 创建同义词

 CREATE SYNONYM d_sum
 FOR dept_sum_vu;


删除同义词

说明:创建同义词必须要拥有 create synonym | create public synonym的权限。
类似别名,多用于分布式的数据库中。