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的权限。
类似别名,多用于分布式的数据库中。