本篇博文仅作笔记阅览,记录了一些SQL的基础操作语法。代码有点多,但都是我曾经一个一个代码敲出来的,笔记中所有样例用测试数据脚本搭好环境后均可运行。
笔记内容结构:
/*==========================================第一章 简单查询语句 ============================================================================*/
SELECT * FROM emp;
SELECT DISTINCT(ename) FROM emp;
SELECT COUNT(*) FROM emp;
SELECT * FROM emp WHERE comm = NULL AND empno = 7396; --null 表示没内容,并不是为0
SELECT * FROM emp WHERE comm IS NOT NULL;
SELECT * FROM emp WHERE NOT comm IS NULL; --与上句实现一样
SELECT * FROM emp WHERE empno = 1234 OR empno = 2345 OR empno = 4567;
SELECT * FROM emp WHERE empno IN (1234, 2345, 4567); --与上句效果一样
SELECT * FROM emp WHERE empno NOT IN (1234, 2345, 4567); --使用not in时列表不能有null值,否则不会有任何结果
SELECT * FROM emp WHERE ename LIKE '_%'; -- _单字符匹配,%任意字符(>=0位)匹配
/*==========================================第二章 单行函数============================================================================*/
-------------------------1、字符函数
--UPPER(列|字符串) --大写
--LOWER(列|字符串) --小写
--INITCAP(列|字符串) --首字母大写
--REPLACE(列|字符串,新的字符串) --新字符串替代旧字符串
--LENGTH(列|字符串) --字符串长度
--SUBSTR(列|字符串,开始点[,长度]) --字符串截取
SELECT ename 原姓名, Substr(ename, 3) 截取后姓名 FROM emp; --开始点为负值则反向截取(倒数3位置往后截取)
SELECT ename, SUBSTR(ename, 0, 3) FROM emp;
SELECT ename, SUBSTR(ename, 1, 3) FROM emp; --上述两条结果一样
--ASCII(字符) --返回指定字符对应的十进制数字
SELECT ASCII('L') FROM dual;
--CHR(数字) --返回数字对应的字符
SELECT CHR(65) FROM dual;
--RPAD(列|字符串,长度,填充字符) --在右填充字符串 长度为补充完字符后总长度
--LPAD(列|字符串,长度,填充字符) --在左填充字符串
SELECT lpad('MLDN', 10, '*') lpad函数使用, rpad('MLDN', 10, '*') rpad函数使用,
lpad(rpad('MLDN', 10, '*'), 16, '*') 组合使用
FROM dual;
--LTRIM(字符串)、RTRIM(字符串)
--去掉左或右空格
--TRIM(列|字符串) --去掉左右空格
--INSTR(列|字符串,要查找的字符串,开始位置,出现位置) --查找一个子字符串是否在指定位置上出现 返回子字符串起点位置
SELECT instr('MLDN Java', 'MLDN') 查找得到, instr('MLDN Java', 'Java') 查找得到,
instr('MLDN Java', 'JAVA')
FROM dual;
-------------------------2、数值函数
--ROUND(数字[,保留位数]) --如果没有指定保留小数位,直接将小数点后1位四舍五入;保留小数位且设置成负数也可实现进位
--TRUNC(数字[,截取位数])
SELECT trunc(789.564) 截取小数, trunc(789.566, 2) 截取两位小数,
trunc(789.564, -2) 取整
FROM dual;
--MOD(数字,数字)
SELECT MOD(10, 3) FROM dual;
-------------------------3、日期函数
--add_months(日期 , 数字) --指定日期加上指定月数
SELECT SYSDATE, add_months(SYSDATE, 3) 三个月之后日期,
add_months(SYSDATE, -3) 三个月之前日期
FROM dual;
--months_between(日期1, 日期2) --两个日期月份间隔
SELECT empno 雇员编号, empname 雇员名称, hiredate 雇员日期,
trunc(month_between(SYSDATE, hiredate)) 雇佣总月份,
trunc(month_between(SYSDATE, hiredate) / 12) 雇佣年数
FROM emp;
--next_day(日期,星期数 ) --求出下一个星期几的具体日期
SELECT SYSDATE, next_day(SYSDATE, '星期日') 下一个星期日,
next_day(SYSDATE, '星期一') 下一个星期一
FROM dual;
--last_day(日期 ) --求出指定日期所在月份的倒数第几天天日期
SELECT SYSDATE, last_day(SYSDATE) - 2 FROM dual;
--用途举例:查询所有在每月倒数第三天雇佣人员
SELECT * FROM emp WHERE last_day(hiredate) - 2 = hiredate;
--extract(格式 from 数据) --日期时间分割,或计算给定两个日期的间隔
--从时间戳取出年月日时分秒
SELECT extract(YEAR FROM systimestamp) years,
extract(MONTH FROM systimestamp) months,
extract(DAY FROM systimestamp) days,
extract(HOUR FROM systimestamp) hours,
extract(minute FROM systimestamp) minutes,
extract(SECOND FROM systimestamp) seconds
FROM dual;
--取得时间间隔
SELECT extract(DAY FROM to_timestamp('1998-08-13 12:14:45',
'yyyy-mm-dd hh24:mi:ss') -
to_timestamp('1997-08-10 13:16:55',
'yyyy-mm-dd hh24:mi:ss')) days,
extract(hour FROM datetime_one - datetime_two) hours,
extract(minute FROM datetime_one - datetime_two) minutes,
extract(SECOND FROM datetime_one - datetime_two) seconds
FROM (SELECT to_timestamp('1998-08-13 12:14:45',
'yyyy-mm-dd hh24:mi:ss') datetime_one,
to_timestamp('1997-08-10 13:16:55',
'yyyy-mm-dd hh24:mi:ss') datetime_two
FROM dual);
-------------------------4、转换函数
--to_char(日期 |数字 |列 , 转换格式) --将指定数据按照指定格式变为字符串型
--to_date(字符串 |列 , 转换格式) --将指定字符串按照指定格式转为date型
--to_number(字符串 |列 ) --将指定数据类型转为数字型
--************************************
--*********日期格式化标记 ************
--************************************
--YYYY 完整的年份表示 Y,YYY 带逗号的年
--YYY 年的后三位 YY 年的后两位
--Y 年的后一位 YEAR 年的文字表示
--MONTH 月份文字表示 MM 月份数字表示2位
--DAY 天数的文字表示 DDD 表示一年里天数(001~365)
--DD 一月里的天数 D 一周里天数
--DY 文字表示星期几 WW 一年里的周数
--W 一月里周数 HH 12小时制
--HH 24小时制 MI 分钟
--SS 秒 SSSSS 午夜之后的秒数字表示(0~86399)
--AM|PM(A.M.|P.M.) 上午/下午 FM 去掉查询后的前导0,该标记用于时间模版的后缀
SELECT SYSDATE 当前系统时间, to_char(SYSDATE, 'YYYY-MM-DD') 格式化日期 ,
to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') 格式化日期时间 ,
to_char(SYSDATE, 'FMYYYY-MM-DD HH24:MI:SS') 去掉前导0的日期时间
FROM dual;
--关于数据库的隐式数据转换操作常用规则:
--1、字符型(varchar2、varchar)如果由数字组成,则可以直接转换成数字(number)
--2、字符型(varchar2、varchar)如果按照指定的日期格式(如 '08-9月 -81'),则可以自动转换为date型数据\
--要注意:数字型 (number)和日期型 (date)之间是不能直接转换的。
--例:下述两条语句相同
SELECT * FROM emp WHERE to_char(hiredate, 'MM') = '02';
SELECT * FROM emp WHERE to_char(hiredate, 'MM') = 2;
--************************************
--*********数字格式化标记 ************
--************************************
--9 表示一位数字
--0 表示前导0
--$ 将货币符号显示为美元符号
--L 根据语言环境不同,自动选择货币符号
--. 显示小数点
--, 显示千位符
--==范例:
SELECT to_char(987654321.789, '999,999,999,999.9999') 格式化数字,
to_char(987654321.789, '000,000,000,000.0000') 格式化数字,
to_char(987654321.789, 'L999,999,999,999.9999') 显示货币,
to_char(987654321.789, '$000,000,000,000.0000') 显示美元
FROM dual;
SELECT to_date('1979-09-19', 'YYYY-MM-DD') FROM dual;
SELECT to_timestamp('1981-09-23 18:07:10', 'YYYY-MM-DD HH24:MI:SS') datetime FROM dual;
SELECT to_number('09') + to_number('19') 加法计算,
to_number('2') * to_number('8') 乘法计算
FROM dual;
-------------------------5、通用函数
--NVL(数字|列 ,默认值) 如果要显示的数字是null,则使用默认数值表示
--NVL2(数字|列 ,返回结果1(不为空显示), 返回结果2(为空显示)) 判断指定的列是否为null, 如果不为null返回1,如果为null返回2
--NULLIF(表达式1, 表达式2) 比较表达式1和表达式2的结果是否相等,如果等返回NULL,如果不等返回表达式1
--DECODE(列 |值 ,判断值1,显示结果1,判断值2,显示结果值2,..,默认值) 多值判断,如果某一个列(或某一个值)与判断值相同,则使用指定的显示结果输出,如果不满足条件,则显示默认值
--CASE 列 |数值 WHEN 表达式1 THEN 显示结果1...ELSE 表达式n... END 用于实现多条件判断,在WHEN之后编写条件,而在THEN之后编写条件满足的显示操作,如果都不满足使用ELSE处理
--COALESCE(表达式1,表达式2,...,表达式n) 将表达式逐个判断,如果表达式1的内容是null,显示表达式2,如果表达式2内容是Null,显示表达式3,一次类推,如果表达式n还是null则返回null。
--=======范例:
--1
SELECT ename, sal,
decode(job, 'CLERK', '业务员', 'SALESMAN', '销售人员', 'MANAGER', '经理',
'PRESIDENT', '总裁') job
FROM emp;
--2
SELECT ename, sal,
CASE job
WHEN 'CLERK' THEN
sal * 1.1
WHEN 'SALESMAN' THEN
sal * 1.2
WHEN 'MANAGER' THEN
sal * 1.3
ELSE
sal * 1.4
END 新工资
FROM emp;
/*==========================================第三章 多表查询 ============================================================================*/
--提升效率要消除笛卡尔积影响(等值判断)。
--表的连接:内连接(只能显示等值满足条件)和外连接(左外连接、右外连接、全外连接)
--左关系属性 = 右关系属性(+),加号放在等式右边,表示左连接,以左表为主表(左表全显示,右表不符合的用null填充)
--左关系属性(+) = 右关系属性,加号在等式左边,表示右连接,以右表为主表(右表全显示,左表不符合的用null填充)
--左右表区分:1、from 表1,表2:其中表1是左表,表2是右表; 2、where 表1.字段 = 表2.字段 此时表1是左表,表2是右表
--实际开发中最好用+标记,但只适用于oracle数据库中。
--自身关联例子:查询雇员信息和领导信息,此处使用左外连接是让没有领导的雇员也显示(领导信息为null)
SELECT e.empno eno, e.ename ename, m.empno mno, m.ename mname
FROM emp e, emp m
WHERE e.mgr = m.empno(+);
------**********SQL:1999语法 **********
--交叉连接(cross join)作用于两个关系,且第一个关系的每个元组与第二个关系所有元组进行连接,结果与笛卡尔积一样
SELECT * FROM emp CROSS JOIN dept;
SELECT * FROM emp;
SELECT * FROM dept;
--自然连接(natural join):只考虑那些在两个关系模式中都出现的属性上取值相同的元组对(共有属性可能有多个)
--如果一个元组不能和另一个关系中的任意元组配对,则称其为悬浮元组。
SELECT * FROM emp NATURAL JOIN dept;
--通过自然连接可以使用关联字段消除笛卡尔积,如果两表没有关联字段,可以使用using子句完成对笛卡尔积消除。
SELECT * FROM emp JOIN dept USING(deptno);
--ON 子句:可以由用户设置关联条件
SELECT *
FROM emp e
JOIN salgrades salg
ON (e.sal BETWEEN salg.losal AND salg.hisal);
--外连接
--使用SQL:1999实现左外连接 与使用 + 结果一样
SELECT * FROM emp;
SELECT * FROM dept;
SELECT * FROM emp e LEFT OUTER JOIN dept d ON (e.deptno = d.deptno);
--使用SQL:1999实现右外连接
SELECT * FROM emp e RIGHT OUTER JOIN dept d ON (e.deptno = d.deptno);
--使用SQL:1999实现全外连接
SELECT * FROM emp e FULL OUTER JOIN dept d ON (e.deptno = d.deptno);
------**********数据的集合运算 **********
--并、交、差、笛卡尔积
--并:UNION | UNION ALL:前者返回若干个查询结果全部内容,但是重复元组不显示,后者会显示重复元组。
--交:INTERSECT:返回若干个查询结果中的相同部分。
--差:MINUS:返回若干个结果集不同部分。
--范例1:dept表中deptno=10的记录只显示一次(若为UNION ALL则会显示多行)
SELECT *
FROM dept
UNION all
SELECT * FROM dept WHERE deptno = 10;
--OR操作表示多个限定条件有一个满足就可,但是从性能上讲,通过UNION UNION ALL要比直接使用OR性能快许多。
--范例2:使用UNION代替OR,查询所有办事员与销售人员信息。
SELECT *
FROM emp
WHERE job = 'CLERK'
UNION
SELECT * FROM emp WHERE job = 'SALESMAN';
--范例2:使用MINUS执行差集操作。(10部门信息重复,所以只返回两个结果集的不同部分)
SELECT *
FROM dept
--INTERSECT
MINUS
SELECT * FROM dept WHERE deptno = 10;
/*==========================================第四章 分组统计查询 ============================================================================*/
--*******3.1:统计函数
--COUNT(* |[DISTINCT] 列 ):求出全部的记录数,如果表中没有数据返回0,其它统计函数返回null.
--SUM(列 ):求出总和 (操作列是数字)
--AVG(列 ):求平均值
--MAX(列 ):
--MIN(列 ):
--MEDIAN(列 ):返回中间值
--VARIANCE(列 ):返回方差
--STDDEV(列 ):返回标准差
--范例:
SELECT AVG(sal), MAX(sal), MIN(sal), ROUND(AVG(sal), 2), MEDIAN(sal) FROM emp;
--*******3.2:单字段分组统计(只设定一个分组条件 )
SELECT DISTINCT 分组字段 AS 别名,...统计函数 AS 别名
FROM 表1, 表2
[WHERE 条件]
[GROUP BY 分组字段]
[ORDER BY 排序字段 ASC | DESC];
SELECT deptno, COUNT(*) FROM emp GROUP BY deptno;
SELECT deptno, MIN(sal), MAX(sal) FROM emp GROUP BY deptno;
--注意:1、如果没有group by子句,则在select子句中只允许出现统计函数,其他任何字段都不允许出现。
--------2、统计查询中,select子句后只允许出现分组字段和统计函数,其它非分组字段不允许出现。如:
SELECT deptno, MAX(sal), MIN(sal) FROM emp GROUP BY deptno; --job字段不能使用
--------3、统计函数允许嵌套使用,但在嵌套统计函数之后的select子句中不允许在出现任何字段,包括分组字段。如:
--求每个部门平均工资最高的工资
SELECT MAX(AVG(sal)) FROM emp GROUP BY deptno;
--由于此时的分组函数进行了嵌套(max(avg(sal))),则在查询中是不能出现任何其他字段的:错误范例:
SELECT deptno, MAX(AVG(sal)) FROM emp GROUP BY deptno; --deptno字段不能出现
--分组规律:
--1、当需要使用分组函数而且又需要查询其他列(分组条件)时,一般都要进行分组统计。
--2、一个列上存在了重复值的时候就都可以使用分组进行操作,这个列可能是具体表的列,也可能是返回的临时表的列。
--*******3.3:多字段分组统计(可设定多个分组条件)
--范例:查询每个部门详细信息:编号、名称、位置、部门人数、平均工资、总工资、最高工资、最低工资
--思路:
--确定所需要的数据表:
---dept:编号、名称、位置
---emp:统计出各部门人数、平均工资、总工资、最高/低工资
---确定已知的关联字段:emp.deptno = dept.deptno.
----步骤一:两表关联,消除笛卡尔积
SELECT d.deptno, d.dname, d.loc, e.empno, e.ename
FROM emp e, dept d
WHERE e.deptno = d.deptno;
----步骤二:上述查询结果中deptno、dname、loc三个字段内容是重复的,所以对上述结果集临时表分组,分组条件三个
SELECT d.deptno, d.dname, d.loc, COUNT(e.empno) 人数,
round(AVG(sal), 2) avgsal, SUM(sal) sumsal, MAX(sal) maxsal,
MIN(sal) minsal
FROM emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
----步骤三:上述结果漏掉了部门号为40的部门(该部门暂时没人):使用右连接完整显示:
SELECT d.deptno, d.dname, d.loc, COUNT(e.empno) 人数,
round(AVG(sal), 2) avgsal, SUM(sal) sumsal, MAX(sal) maxsal,
MIN(sal) minsal
FROM emp e, dept d
WHERE e.deptno(+) = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
----步骤四:上述结果中部门40的字段显示为Null,使用NVL()函数将所有的null变为0
SELECT d.deptno, d.dname, d.loc, nvl(COUNT(e.empno), 0) 人数,
nvl(round(AVG(sal), 2), 0) avgsal, nvl(SUM(sal), 0) sumsal, nvl(MAX(sal), 0) maxsal,
nvl(MIN(sal), 0) minsal
FROM emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
SELECT * FROM dept;
--*******3.4:HAVING子句
--如:选出部门人数超过5人的部门信息,这样操作先按照部门进行分组统计,再对统计结果进行过滤,可用HAVING子句过滤
--范例1:
SELECT job, round(AVG(sal), 2), COUNT(empno)
FROM emp
GROUP BY job
HAVING(sal) > 2000;
--范例2:列出至少有一个员工的部门信息
SELECT d.deptno, d.dname, round(AVG(sal), 2) avgsal, MAX(sal) maxsal,
MIN(sal) minsal
FROM emp e, dept d
WHERE e.deptno(+) = d.deptno
GROUP BY d.deptno, d.dname, d.loc
HAVING COUNT(e.empno) > 1;
/*浅析where子句和having子句异同:
同:两者都是过滤作用
异:where子句:在分组之前使用,表示从所有数据中筛选出部分数据,以完成分组的要求,
在where子句中不允许使用统计函数,没有group by子句也可以使用.
having子句:是在分组之后使用的,表示对分组统计后的数据执行再次过滤,可以使用统计函数,
有group by 子句之后才可以使用having子句。
*/
--范例:显示非销售人员的工作名称、以及同一工作雇员月工资总和,且月工资大于5000,最后结果按月工资总和升序排列:
SELECT job, SUM(sal) sumsal
FROM emp
WHERE job <> 'SALESMAN'
GROUP BY job
HAVING SUM(sal) > 5000
ORDER BY sumsal ASC;
/*==========================================第五章 子查询 ============================================================================*/
--语法:
SELECT [DISTINCT]* | 分组字段1 [AS ] [列别名], [分组字段2 [AS] [列别名], ...],(
SELECT [DISTINCT]* | 分组字段1 [AS ] [列别名], [分组字段2 [AS] [列别名], ...]
FROM 表1 [表别名1], 表2 [表别名2],...
[WHERE 条件(s)]
[GROUP BY 分组字段1, 分组字段2,...]
[HAVING 过滤条件(s)]
[ORDER BY 排序字段 ASC | DESC] )...
FROM 表1 [表别名1], 表2 [表别名2],...
SELECT [DISTINCT]* | 分组字段1 [AS ] [列别名], [分组字段2 [AS] [列别名], ...]
FROM 表1 [表别名1], 表2 [表别名2],...
[WHERE 条件(s)]
[GROUP BY 分组字段1, 分组字段2,...]
[HAVING 过滤条件(s)]
[ORDER BY 排序字段 ASC | DESC]
[WHERE 条件(s)... (
SELECT [DISTINCT]* | 分组字段1 [AS ] [列别名], [分组字段2 [AS] [列别名], ...]
FROM 表1 [表别名1], 表2 [表别名2],...
[WHERE 条件(s)]
[GROUP BY 分组字段1, 分组字段2,...]
[HAVING 过滤条件(s)]
[ORDER BY 排序字段 ASC | DESC])]
[GROUP BY 分组字段1, 分组字段2,...]
[HAVING 过滤条件(s)...(
SELECT [DISTINCT]* | 分组字段1 [AS ] [列别名], [分组字段2 [AS] [列别名], ...]
FROM 表1 [表别名1], 表2 [表别名2],...
[WHERE 条件(s)]
[GROUP BY 分组字段1, 分组字段2,...]
[HAVING 过滤条件(s)]
[ORDER BY 排序字段 ASC | DESC])]
[ORDER BY 排序字段asc | DESC];
/*可发现,子查询几乎可以出现在一条查询语句的任意位置上,但一般在from、where、having子句中出现较多,在
使用子查询时一定要使用()声明,子查询出现位置:
where子句:此时子查询结果一般都是单行单列、单行多列、多行单列
having子句:此时子查询结果一般都是单行单列数据,同时为了使用统计函数操作
from子句:此时子查询结果一般都是多行多列,可以按照一张数据表(临时表)的形式操作
*/
--*******5.1:在where子句中使用子查询
--***5.1.1子查询返回单行单列数据:
--范例1:查出基本工资比ALLEN低的雇员信息:
SELECT * FROM emp WHERE sal < (SELECT sal FROM emp WHERE ename = 'ALLEN');
--范例2:查询基本工资高于公司平均工资的雇员信息:
SELECT * FROM emp WHERE sal > (SELECT AVG(sal) FROM emp);
--范例3:查询工作与ALLEN相同,工资高于工号为7521的雇员信息:
SELECT *
FROM emp
WHERE job = (SELECT job FROM emp WHERE ename = 'ALLEN')
AND sal > (SELECT sal FROM emp WHERE empno = 7521);
--***5.1.2子查询返回单行多列数据:
--范例1:查询与SCOTT从事同一工作且工资相同的雇员信息:
SELECT *
FROM emp
WHERE (job, sal) = (SELECT job, sal FROM emp WHERE ename = 'SCOTT')
AND ename <> 'SCOTT';
--范例2:查询与雇员7521从事同一工作且领导相同的雇员信息:
SELECT *
FROM emp
WHERE (job, mgr) = (SELECT job, mgr FROM emp WHERE empno = 7521)
AND empno <> 7521;
--范例3:查询与ALLEAN从事同一工作且同一年雇佣的职员信息:
SELECT *
FROM emp
WHERE (job, to_char(hiredate, 'yyyy')) =
(SELECT job, to_char(hiredate, 'yyyy')
FROM emp
WHERE ename = 'ALLEAN')
AND ename <> 'ALLEN';
--***5.1.3子查询返回多行单列数据(in any all操作符):
--范例1:查询出每个部门中最低工资相同的全部雇员信息(不同则not in):
SELECT * FROM emp WHERE sal IN (SELECT MIN(sal) FROM emp GROUP BY deptno);
--注意如果使用in操作符在指定的查询范围中存在一个null,则将不会有任何的查询结果返回
--范例2:因为emp中mgr字段有null存在,下述查询不会有返回结果显示
SELECT e.ename FROM emp e WHERE e.empno NOT IN (SELECT m.mgr FROM emp m);
--***ANY 操作符: =ANY(与in类似 ), >ANY(比子查询返回结果最小值还要大,包含了>=ANY ) <ANY(比子查询返回最大结果还要小,包含了<=ANY)
-- <>ANY 并不和not in类似,<>ANY 会返回所有结果。
--范例出的是薪资小于任一岗位为MANAGER的雇员信息(返回结果是比子查询中的最大值还要小的全部数据)
SELECT *
FROM emp
WHERE sal < ANY
(SELECT MIN(sal) FROM emp WHERE job = 'MANAGER' GROUP BY deptno);
--***ALL操作符: <>ALL(等价于not in) >ALL(比子查询中最大值还要大) <ALL(比子查询中最小值还要小)
--范例:
SELECT *
FROM emp
WHERE sal > ALL
(SELECT MIN(sal) FROM emp WHERE job = 'MANAGER' GROUP BY deptno);
--***空数据判断(exists与not exists)
--范例:如果没有雇员号9999的员工,exists返回false,最后不会有数据返回
SELECT * FROM emp WHERE EXISTS(SELECT * FROM emp WHERE empno=9999);
--*******5.2:在having子句中使用子查询
--范例1:查询部门编号、雇员人数、平均薪资,并要求这些部门平均薪资高于公司平均薪资。
SELECT d.deptno, COUNT(e.empno) AVG(e.sal) avgsal
FROM emp e, dept d
WHERE d.deptno = e.deptno
GROUP BY d.deptno
HAVING avgsal > (SELECT AVG(sal) FROM emp);
--*******5.3:在from子句中使用子查询
--范例1:查询每个部门的编号、名称、位置、部门人数、平均工资。
SELECT d.deptno, d.dname, d.loc, temp.cnt, temp.avgsal
FROM dept d,
(SELECT deptno dno, COUNT(empno) cnt, round(AVG(sal), 2)) avgsal
FROM emp
GROUP BY deptno) temp
WHERE d.deptno = temp.dno(+);
--法2:
SELECT d.deptno, d.dname, d.loc COUNT(e.empno) cnt,
round(AVG(sal), 2) avgsal
FROM emp e, dept d
WHERE e.deptno(+) = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
--运用子查询的好处是可以提升多表查询性能。
--范例:查询出所有在部门SALES工作的员工相关信息和部门最高、最低工资。
--步骤1:查询出销售部编号
SELECT deptno FROM dept WHERE dname = 'SALES';
--步骤2:由于查询返回单行单列数据,所以可以在where子句中使用子查询查出雇员信息
SELECT empno, ename, sal, comm, job, hiredate
FROM emp
WHERE deptno = (SELECT deptno FROM dept WHERE dname = 'SALES');
--步骤 3:
SELECT e.empno, e.ename, e.sal, e.comm, e.job, e.hiredate, temp.maxsal,
temp.minsal
FROM emp e,
(SELECT deptno dno, MAX(sal) maxsal, MIN(sal) minsal
FROM emp
GROUP BY deptno) temp
WHERE e.deptno = (SELECT deptno FROM dept WHERE dname = 'SALES')
AND e.deptno = d.deptno;
--范例:查询出所有薪资高于公司平均薪金的员工信息,所在部门名称,位置,领导姓名,公司的工资等级,
-- 部门人数,平均工资,平均服务年限
/*确定所需数据表:emp:员工信息
dept表:部门信息
emp表:领导信息
salgrade表:工资等级信息
emp表:统计出部门人数、平均工资、平均服务年限
确定已知的关联字段;
雇员和部门:emp.deptno = dept.deptno
雇员和领导: emp.mgr = memp.empno
雇员和工资等级: emp.sal between salagrade.losal and salgrade.hisal
*/
SELECT e.empno, e.ename, e.sal, e.job, e.hiredate, d.dname, d.loc,
m.ename mname, select.grade, temp.cnt, temp.avgsal, temp.avgyear
FROM emp e, dept d, emp m, salgrade s,
(SELECT deptno dno, COUNT(empno) COUNT, round(AVG(sal), 2) avgsal,
round(AVG(months_between(SYSDATE, hiredate) / 12), 2) avgyear
FROM emp
GROUP BY deptno) temp
WHERE e.sal > (SELECT AVG(sal) FROM emp)
AND e.deptno = d.deptno
AND e.mgr = m.empno(+)
AND e.sal BETWEEN s.losal AND s.hisal
AND e.deptno = temp.dno;
--范例:列出薪金比allen或clerk多的所有员工编号、姓名、基本工资、部门名称、领导姓名、部门人数。
SELECT e.empno, e.ename, e.sal, d.dname, m.ename mname, temp.cnt
FROM emp e, dept d, emp m,
(SELECT deptno dno, COUNT(empno) cnt FROM emp GROUP BY deptno) temp
WHERE e.sal > ANY (SELECT sal FROM emp WHERE ename IN ('CLERK', 'ALLEN'))
AND e.ename NOT IN ('ALLEN', 'CLERK')
AND d.deptno = e.deptno
AND e.mgr = m.empno(+)
AND e.deptno = temp.dno;
--范例:列出公司各个部门经理姓名、薪金、部门名称、部门人数、平均工资。
SELECT e.ename, e.sal, d.dname, temp.cnt, temp.avgsal
FROM emp e, dept d,
(SELECT deptno dno, COUNT(empno) cnt, AVG(sal) avgsal
FROM emp
GROUP BY deptno) temp
WHERE e.job = 'MANAGER'
AND e.deptno = d.deptno
AND e.deptno = temp.dno;
--*******5.4:在select子句中使用子查询
--范例:查询公司每个部门编号、名称、位置、部门人数、平均工资。
SELECT d.deptno, d.dname, d.loc,
(SELECT COUNT(empno) FROM emp WHERE deptno = d.deptno) 部门人数,
(SELECT AVG(sal) FROM emp WHERE deptno = d.deptno) 平均工资
FROM dept d;
--*******5.4:WITH 子句
--with子句提供了一种定义临时表的操作方法,如果一个查询中要反复用到一些数据,就可以将其定义在with子句中
--范例:使用with子句将emp表中数据定义为临时表
WITH e AS
(SELECT * FROM emp)
SELECT * FROM e;
--范例:查询每个部门编号、名称、位置、平均薪资、人数
WITH e AS
(SELECT deptno dno, round(AVG(sal), 2) avgsal, COUNT(empno) cnt
FROM emp
GROUP BY deptno)
SELECT d.deptno, d.dname, d.loc, e.avgsal, e.cnt
FROM e, dept d
WHERE e.dno(+) = d.deptno;
/*==========================================第六章 更新及事务处理 ============================================================================*/
--******6.1:增删改
--复制表
CREATE TABLE mytab AS SELECT * FROM emp;
--查看当前用户的全部表(通用)
SELECT * FROM tab;
--增
INSERT INTO mytab(列1, 列2,列3,...) VALUES(v1, v2, v3,...);
INSERT INTO mytab [AS] SELECT * FROM emp WHERE deptno = 7521;
--更
--范例1:由用户指定更新数据
UPDATE mytab SET sal = 3000, comm = 500 WHERE empno = 3455;
UPDATE mytab SET sal=sal*1.2 WHERE sal < (SELECT avg(sal) FROM mytab);
--范例2:使用已有数据更新数据表
UPDATE mytab
SET (job, sal, hiredate) =
(SELECT job, sal, hiredate FROM mytab WHERE empno = 3456)
WHERE empno = 2345;
--******6.2:事务处理
/*ACID特性:原子性(atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
每个连接到服务器上用户在Oracle中都使用一个session概念表示,每个session拥有独立的事务操作
*/
/*替代变量:由用户输入数据
*/
--范例:
SELECT * FROM emp WHERE empno = &epno;
SELECT * FROM emp WHERE ename = UPPER('&inputename'); --解决输入为字符串时单引号问题
SELECT * FROM emp WHERE ename LIKE UPPER('&inputkeyword') AND sal > (&inputsal); --输入关键字进行查询
/*替代变量出现位置*/
--1、SELECT子句使用
SELECT &inputColName FROM emp WHERE deptno = &inputDeptno;
--2、FROM子句中
SELECT * FROM &inputTabName;
--3、ORDER BY子句中使用
SELECT * FROM emp WHERE deptno = 20 ORDER BY &inputOrderByCol DESC | ASC;
--4、GROUP BY子句中使用:以使用"&&"开头只需输入一次(因为select子句和group by子句输入的替代变量内容一致)
SELECT &inputGroupByCol, SUM(sal), AVG(sal) FROM emp e GROUP BY &inputGroupByCol;
SELECT &&inputGroupByCol, SUM(sal), AVG(sal) FROM emp e GROUP BY &inputGroupByCol;
--使用“&&”相当于在SQLPLUS定义了一个变量,而这个变量失效情况有两种:
--1、一个SQLPLUS窗口关闭后失效。
--2、调用了UNDEFINE命令失效
--范例:取消替代变量,这样在重复执行替代变量时,才会提示用户重新输入新的替代变量。如果不需要设置任何替代变量可以使用:
--set define off命令表示不定义
undefine inputGroupByCol;
--定义替代变量:
define inputdname = 'ACCOUTING';
--查询替代变量
define inputdname;
--使用替代变量
SELECT * FROM dept WHERE dname = '&intputdname';
--清除替代变量
undefine inputdname;
/*
ACCEPT命令:ACCEPT 替代变量名称 [数据类型] [FORMAT 格式] [PROMPT '提示信息'] [HIDE]
该命令只能在脚本文件中使用
*/
--范例1
ACCEPT inputEname PROMPT '请输入要查询信息的雇员姓名:'
SELECT * FROM emp WHERE ename = UPPER('&inputEname');
--范例2:加hide会隐藏用户输入
ACCEPT inputEname PROMPT '请输入要查询信息的雇员姓名:' HIDE
SELECT * FROM emp WHERE ename = UPPER('&inputEname');
--范例3:使用FORMAT限定用户输入长度:输入超过10个长度的字符会报错
ACCEPT inputEname PROMPT '请输入要查询信息的雇员姓名:' FORMAT A10
SELECT * FROM emp WHERE ename = UPPER('&inputEname');
--范例4:使用FORMAT限定用户格式化输入
ACCEPT inputDate DATE FORMAT 'YYYY-MM-DD' PROMPT '请输入要查询的日期:'
SELECT * FROM emp e WHERE hiredate = TO_DATE('&inputDate', 'YYYY-MM-DD');
/*==========================================第七章 表的创建与管理 ============================================================================*/
/*
Oracle常用数据类型:
char(n) n = 1 to 2000(字节) 保存定长字符串
varchar(n) n = 1 to 4000(字节) 可以存放数字、字母及ASCII字符集
number(m, n) m = 1 to 38 n = -84 to 127 表示数字,其中小数部分为 n位,整数部分为m - n位
date - 用于存放日期时间型数据(不包含毫秒)
timestamp - 用于存放日期时间型数据(不包含毫秒)
clob(Character Large Object) 4G 用于存放海量文字,如保存一本书
blob(Binary Large Object) 4G 用于保存二进制文件,如图片、电影、音乐等。
*/
--表的复制
CREATE TABLE department
AS
SELECT d.deptno deptno, d.dname dname, d.loc loc, COUNT(e.empno) cnt,
SUM(e.sal + nvl(e.comm, 0)) sumsal
FROM dept d, emp e
WHERE d.deptno = e.deptno(+)
GROUP BY d.deptno, d.dname, d.loc
ORDER BY d.deptno;
--查看表结构:describe(desc)
--表重命名:rename 表旧名 to 表新名;
--截断表:truncate table 表名称; 表中所占资源全部释放,数据清空,而且所有数据也不可以使用事务进行回滚处理。
--一般很少用到该命令。
--另一种方法:delete from 表名称;
--删除表:drop table 表名称;
/*
闪回技术:oracle 10g后,防止用户误删表数据,提供了一个类似windows 回收站的功能,用户删除表先放在此处,可从此恢复。
*/
--先删除表
DROP TABLE emp;
DROP TABLE dept;
--查看所有表:TNAME栏会有以BIN$XXX开头的数据
SELECT * FROM tab;
--查看回收站数据:
SELECT object_name, original_name, operation, TYPE FROM RECYCLEBIN;
--从回收站中恢复表:FLASHBACK TABLE 表名 TO BEFORE DROP;
FLASHBACK TABLE emp TO BEFORE DROP;
FLASHBACK TABLE dept TO BEFORE DROP;
SELECT object_name, original_name, operation, TYPE FROM RECYCLEBIN; --再次查询回收站中表
--直接删除表:drop table 表名 purge; 这样删除将不会保存在回收站中。
--从回收站中删除表:purge table 表名;
--清空回收站: purge recyclebin;
--****修改表结构
--1、为表增加字段
ALTER TABLE 表名 ADD(字段名 字段类型 DEFAULT 默认值, 字段名 字段类型 DEFAULT 默认值...);
ALTER TABLE emp ADD(sex VARCHAR2(10) DEFAULT '男');
--2、修改表中字段
ALTER TABLE 表名 MODIFY(字段名 字段类型 DEFAULT 默认值);
ALTER TABLE emp MODIFY(sex VARCHAR(4) DEFAULT '女');
--3、删除表中字段
DROP TABLE 表名 DROP COLUMN 列名;
ALTER TABLE emp DROP COLUMN sex;
/*
如果在一个比较庞大的数据表中(千万行数据)执行列删除操作会非常耗时,为了保证使用又提升效率可以将列设置为
无用状态,再查看表结构或查询时都不会有该字段显示。
*/
ALTER TABLE 表名 SET UNUSED(列名);
ALTER TABLE 表名 SET UNUSED COLUMN 列名;
ALTER TABLE emp SET UNUSED(comm);
SELECT * FROM emp;
/*
为表或列添加注释:
*/
COMMENT ON COLUMN emp.empno IS '雇员编号'
COMMENT ON TABLE 表名 | COLUMN 表名.列名 IS '注释内容';
--可以通过查看user_tab_comments数据字典查看注释
SELECT * FROM User_Tab_Comments WHERE comments IS NOT NULL;
COMMENT ON TABLE emp IS '雇员信息表';
COMMENT ON COLUMN emp.empno IS '雇员编号';
SELECT * FROM user_tab_comments WHERE table_name = 'EMP';
/*
设置可见/不可见字段:如果某些数据列内容不需要使用到,直接为其设置null即可,日后需要增加若干个列,
如果提前增加的话会对开发人员造成困扰,为此就可以将其设置为不可见状态,待日后需要时再设置为可见状态。
*/
/*
范例:设置为不可见/可见后,desc 表名 将不会显示/显示该字段,但可以通过user_tab_columns数据字典查看.
设为不可见后增删改查时就可以忽略 该字段的存在。
*/
ALTER TABLE 表名 MODIFY(字段 [INVISIBLE | VISIBLE]);
ALTER TABLE emp MODIFY(ename INVISIBLE);
SELECT * FROM User_Tab_Columns WHERE table_name = '表名';
INSERT INTO emp VALUES(1); --假设emp只有ename与empno两个字段,ename不可见
ALTER TABLE emp MODIFY(ename VISIBLE); --恢复可见
/*
表空间:
*/
CREATE [TEMPORARY] TABLESPACE 表空间名
[DATAFILE | TEMPFILE 表空间文件保存路径] [SIZE 数字[K | M]]
[AUTOEXTEND ON | OFF] [NEXT 数字[K | M]]
[LOGGING | NOLOGGING];
/*
TEMPFILE:保存临时表空间的磁盘路径。
LOGGING/NOLOGGING:是否需要对DML进行日志记录,记录下的日志可用于数据恢复。
*/
--范例:使用dba_tablespaces查看表空间信息
SELECT * FROM dba_tablespaces;
--范例:使用dba_data_files查看数据文件信息
SELECT * FROM Dba_Data_Files;
--范例:使用dba_temp_files数据字典查看数据文件信息
SELECT * FROM Dba_Temp_Files;
--范例:创建数据表并使用表空间
CREATE TABLE 用户名.表名(
字段名 字段类型 DEFAULT 默认值,
字段名 字段类型 DEFAULT 默认值
) TABLESPACE 表空间名;
/*==========================================第八章 完整性约束 ============================================================================*/
/*
开发中使用以下5种约束进行定义:
非空约束:字段内容不允许为null.
唯一约束:此列内容不允许重复.
主键约束:表示一个唯一的标识.
检查约束:用户自行编写设置内容的检查条件.
主-外键约束:在两张表上进行的关联约束,加入关联约束后就产生父子关系
*/
--*******8.1:非空约束 NK
--范例1:定义member_test表,其中姓名不许为空
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL
);
--向表中插入数据:第一条执行正确,后两条错误
INSERT INTO member_test VALUES(12, 'admin');
INSERT INTO member_test VALUES(13, NULL);
INSERT INTO member_test VALUES(14);
--*******8.2:唯一约束 UK
-- 范例:创建member_test表,email设置唯一约束
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL,
email VARCHAR(200) UNIQUE
);
--范例:为唯一约束指定一个名字
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL,
email VARCHAR(200,
CONSTRAINT uk_email UNIQUE(email)
);
--上述两个示例后者更好,当出现违反约束情况时,报错更清楚!
--*******8.3:主键约束PK
--如果一个字段要求唯一和非空,则可以使用主键约束:NK + UK
--范例1:
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER PRIMARY KEY,
NAME VARCHAR(200) NOT NULL,
email VARCHAR(200,
CONSTRAINT uk_email UNIQUE(email)
);
--范例2:
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL,
email VARCHAR(200,
CONSTRAINT uk_email UNIQUE(email),
CONSTRAINT pk_mid PRIMARY KEY(mid)
);
--上述两种主键约束后者更好:报错更清楚!
--*******8.4:检查约束CK
/*
检查约束指的是对数据增加的条件进行过滤,表中每行数据都必须满足过滤条件。
*/
--范例1:使用两种方式设置检查约束(age和sex),sex的设置更好:报错清楚!
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL,
email VARCHAR(200,
age NUMBER CHECK(age BETWEEN 0 AND 200),
sex VARCHAR(10)
CONSTRAINT uk_email UNIQUE(email),
CONSTRAINT pk_mid PRIMARY KEY(mid),
CONSTRAINT ck_sex CHECK(sex IN ('男', '女'))
);
--*******8.5:主外键约束
/*
外键约束:通过foreign key,表示advice表中的mid字段取值将受到member_test表中mid字段限制,
当advice_test表插入错误数据 (表member_test中不存在对应mid值)时,
将会报错:ORA-02291:违反完整性约束条件(XXXX)-未找到父项关键字。
简写方法:mid number references member_test(mid)
*/
--范例:
DROP TABLE member_test PURGE;
DROP TABLE advice_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL,
CONSTRAINT pk_mid PRIMARY KEY(mid)
);
CREATE TABLE advice_test(
adid NUMBER,
CONTENT CLOB NOT NULL,
mid NUMBER,
CONSTRAINT pk_adid PRIMARY KEY(adid),
CONSTRAINT fk_mid FOREIGN KEY(mid) REFERENCES MEMBER_test(mid)
);
/*
在子表中设置的与父表关联的外键字段时,这个字段在父表中必须具有主键约束或唯一约束才可以设置成功。
*/
/*
问题1:删除父表记录前需要先删除所有子表的对应记录。
范例:删除member_test中mid为1的数据,因为advice_test中存在与之对应数据,所以执行时会报错:
ORA-02292:违反完整性约束条件(XXX)-已找到子记录。可以先删除子表对应记录,再删除父表记录。
但这种方法较为麻烦。
级联:删除父表数据时子表对应数据的处理:on delete cascade和 on delete set null
*/
DELETE FROM advice_test WHERE mid = 1;
DELETE FROM member_test WHERE mid = 1;
/*
级联删除:指的是在建立外键约束时通过on delete cascade子句设置,这样在删除父表数据时,由父表数据关联的所有子表
数据都会被同时删除。
做法:在添加主外键约束后加入 on delete cascade即可
*/
CREATE TABLE advice_test(
adid NUMBER,
CONTENT CLOB NOT NULL,
mid NUMBER,
CONSTRAINT pk_adid PRIMARY KEY(adid),
CONSTRAINT fk_mid FOREIGN KEY(mid) REFERENCES MEMBER_test(mid) ON DELETE CASCADE
);
/*
【级联设置null】:级联删除能将子表中对应记录也一并删除,但有时候并不希望这样,如删除一个部门并不能把
该部门员工信息删掉。
对于外键级联操作,SQL中可以通过on delete set null选项将子表数据级联设置为null,即当父表数据删除时,子表
数据可以不用删除,并将与父表关联字段内容设置为null
*/
CREATE TABLE advice_test(
adid NUMBER,
CONTENT CLOB NOT NULL,
mid NUMBER,
CONSTRAINT pk_adid PRIMARY KEY(adid),
CONSTRAINT fk_mid FOREIGN KEY(mid) REFERENCES MEMBER_test(mid) ON DELETE SET NULL
);
/*
问题2:删除父表时先将子表删除(与问题1区别开:一个删数据,一个删表)
*/
--范例:先删除子表再删除父表
DROP TABLE advice_test PURGE;
DROP TABLE member_test PURGE;
--范例:有时删除子表可能比较麻烦(如果库里有很多子表,开发者并不了解情况),可以考虑强制性删除,将不关心关联关系。
DROP TABLE member_test CASCADE CONSTRAINT;
--*******8.6:查看约束
/*
Oracle中所有数据对象都是通过数据字典进行记录的,用户设置的约束可以查看user_constraints。
constraint_type表示约束的简写:
P:primary key
R:foreign key
C:check或 not null
Q:unique
*/
SELECT constraint_name, constraint_type, table_name FROM User_Constraints;
--*******8.7:修改约束
--1、为表增加约束
ALTER TABLE 表名 ADD CONSTRAINT 约束名 约束类型(约束字段);
ALTER TABLE member_test ADD CONSTRAINT pk_mid PRIMARY KEY(mid);
ALTER TABLE member_test ADD CONSTRAINT ck_sex CHECK(sex IN ('男', '女'));
--非空约束处理与其它不同
ALTER TABLE member_test MODIFY(NAME VARCHAR(200) NOT NULL);
--2、启用 /禁用约束:当约束很多时,影响oracle性能,大规模加数据时为提升效率可以考虑将约束暂时关闭。
ALTER TABLE 表名 DISABLE CONSTRAINT 约束名 [CASCADE];
ALTER TABLE 表名 ENABLE CONSTRAINT 约束名称;
ALTER TABLE advice_test DISABLE CONSTRAINT pk_adid;
--3、删除约束
ALTER TABLE 表名 DROP CONSTRAINT 约束名 [CASCADE];
ALTER TABLE advice DROP CONSTRAINT pk_adid; --无关联外键
ALTER TABLE advice DROP CONSTRAINT pk_adid CASCADE; --有关联外键
/*==========================================第九章 其它数据库对象 ============================================================================*/
/*
数据表和约束是Oracle的对象,但是在Oracle中对象类型有许多,如视图、同义词、序列、索引等,实际开发中
这些概念也非常重要。
*/
/*
-----9.1:视图
有经验的数据库设计人员往往会为开发者将各个复杂的SQL语句封装为一个个视图,这样开发人员只需要通过
调用视图就可以实现复杂查询功能,提高开发效率。
视图是从一个或几个实体表导出的虚拟表,数据库中只存放视图定义,不存放视图的数据,其数据存放在原来的实体表中。
视图就像一个窗口,通过它可以看到数据库中自己感兴趣的数据以及其变化。
*/
/*
9.1.1 创建视图
FORCE:表示要创建视图的表不存在也可以创建视图
NOFORCE(默认):表示要创建视图的表存在才可以创建。
OR REPLACE:表示视图的替换。如果视图不存在,创建新的视图,若存在,将其替换(最好加上方便维护)。
*/
CREATE [FORCE | NOFORCE] [OR REPLACE] VIEW 视图名称 [(别名1, 别名2,...)]
AS
子查询;
--范例1:创建一张基本工资大于2000的雇员信息的视图
CREATE VIEW v_myview
AS
SELECT * FROM emp WHERE sal > 2000;
--查看tab数据字典看是否创建成功
SELECT * FROM tab WHERE tabtype = 'VIEW';
--也可以查看user_views直接得到视图具体信息
SELECT * FROM user_views;
--查询v_myview视图
SELECT * FROM v_myview;
--注意:如果创建视图时提示权限不足则以系统管理员登录后授权用户:grant create view to 用户名;
--为视图中查询的列起别名
CREATE OR REPLACE VIEW v_myview
(部门编号, 部门名称, 位置, 人数, 平均工资, 总工资, 最高工资, 最低工资)
AS
SELECT d.deptno, d.dname, d.loc, COUNT(e.empno), round(AVG(sal), 2),
SUM(sal), MAX(sal), MIN(sal)
FROM emp e, dept d
WHERE e.deptno(+) = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
/*
-----9.1.2:视图上执行DML操作
*/
--1、简单视图
CREATE OR REPLACE VIEW v_myview
AS
SELECT empno, ename, job, sal, deptno FROM emp WHERE deptno = 20;
/*
增 :在视图和 emp表中都会增加相应记录,但emp表只保存部分信息
删:删除视图和emp表中相应记录
改:更新视图和emp表中相应记录
*/
INSERT INTO v_myview(empno, ename, job, sal, deptno) VALUES(6688, 'asdf', 'clerk', 1900, 20);
UPDATE v_myview SET ename = 'gdsa', job = 'manager', sal = 2300 WHERE empno = 6688;
DELETE FROM v_myview WHERE empno = 6688;
/*
复杂视图:
对于复杂视图,因为涉及到多个表,创建和修改时,无法正常此操作,删除可以正常执行。
*/
CREATE OR REPLACE VIEW v_myview
AS
SELECT e.empno, e.ename, e.job, e.sal, d.deptno, d.dname, d.loc
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND d.deptno = 20;
--当试图给上述视图添加或更新数据时,可能会报错:ORA-01776:无法通过联结视图修改多个基表
DELETE FROM v_myview WHERE deptno = 20;
DELETE FROM v_myview WHERE empno = 7369;
--上述删除操作虽然是针对于视图,但也会影响原始表数据。
/*
-----9.1.3 WITH CHECK OPTION子句
防止违规操作
*/
--范例:
CREATE OR REPLACE VIEW v_emp20
AS
SELECT * FROM emp WHERE deptno = 20;
UPDATE v_emp20 SET deptno = 40 WHERE empno = 7369;
/*
执行上述更新后,v_emp20视图中无法查找到雇员编号是7369的雇员信息,而在emp表中,该雇员部门编号被更新为40.
问题:视图本身功能只是封装了一条查询的SQL语句,而现在的更新操作属于更新视图的创建条件(deptno=20),这种
做法很不合理,使用WITCH CHECK OPTION避免
*/
CREATE OR REPLACE VIEW v_emp20
AS
SELECT * FROM emp WHERE deptno = 20
WITH CHECK OPTION CONSTRAINT v_emp20_ck;
/*
这样再更新视图,将empno为7369的雇员部门编号修改为40将会报错:
ORA-01402:视图with check option where子句违规
*/
/*
9.1.4: WITH READ ONLY 子句
让视图中所有字段不可更新
*/
CREATE OR REPLACE VIEW v_emp20
AS
SELECT * FROM emp WHERE deptno = 20
WITH READ ONLY;
--当执行更新操作或插入操作时,将会报错:ORA-42399:无法对只读视图执行DML操作
--删除视图
DROP VIEW v_emp20;
/*
-----9.2:序列
利用序列可实现数据表流水号的操作(自动增长)
*/
CREATE SEQUENCE 序列名
[INCREMENT BY 步长]
[START WITH 开始值]
[MAXVALUE 最大值 | NOMAXVALUE]
[MINVALUE 最小值 | NOMINVALUE]
[CYCLE | NOCYCLE]
[CACHE 缓存大小 | NOCACHE];
--范例:创建默认序列
CREATE SEQUENCE myseq;
SELECT * FROM User_Sequences;
/*
主要参数:
cycle_flag:循环标记,如果循环序列则显示 Y , 否则显示N
cache_size:序列操作的缓存量(默认20)
last_number:上次操作值
如果用户要使用一个已经创建完成的序列,则可以使用序列中提供的两个伪列进行操作:
序列名.currval:取得当前序列已经增长的结果,重复调用多次后序列内容不会变化,同时当前序列大小(last_number)不变
序列名.nextval:取得序列的下一个增长值,每调用一次序列都会自动增长。
*/
SELECT myseq.nextval FROM dual; --执行一次值增1
SELECT myseq.currval FROM dual; --每次执行值不变
--序列的currval属性要在执行了nextval属性之后才可以使用。
--9.2.1范例:序列和表的结合使用
DROP TABLE member_test PURGE;
CREATE TABLE member_test(
mid NUMBER,
NAME VARCHAR(200) NOT NULL,
CONSTRAINT pk_mid PRIMARY KEY(mid)
);
SELECT * FROM member_test;
--mid自增,name用户输入
INSERT INTO member_test(mid, NAME) VALUES(myseq.nextval, UPPER('&name'));
--9.2.2序列删除
DROP SEQUENCE 序列名;
DROP SEQUENCE myseq;
SELECT * FROM user_sequences;
--9.2.3创建特殊功能的序列
--1、设置序列的增长步长increment by,步长为3,每次调用nextval属性将会增加3
DROP SEQUENCE myseq;
CREATE SEQUENCE myseq INCREMENT BY 3;
SELECT myseq.nextval FROM dual;
--2、设置序列初始值start with
DROP SEQUENCE myseq;
CREATE SEQUENCE myseq
INCREMENT BY 1
START WITH 200;
SELECT myseq.nextval FROM dual;
SELECT myseq.currval FROM dual;
/*
3、设置序列的缓存大小:
默认大小为20,如果一个序列默认从1开始增长,实际在缓存中序列数据已经增长到了21(last_number),如果用户
一直操作的序列内容没有超过21,则序列的last_number不会变,如果序列值已经是21了,则会自动再生成20个序列值
供用户使用,此时last_number值为41.
设置缓存有可能造成跳号情况:序列的真正数值是缓存中所保存内容,当数据库实例重新启动后,缓存中保存数据会消失,
这样在进行序列操作时就可能发生跳号问题,造成序列的不连贯,可以使用nocache声明为不缓存。
*/
DROP SEQUENCE myseq;
CREATE SEQUENCE myseq NOCACHE;
/*
4、设置循环序列
循环序列指的是一个序列在每次调用nextval属性之后可以产生指定范围内的数据。需要指定最大、最小、是否为循环等属性。
*/
--范例:序列建立完成当用户重复执行myseq.nextval属性操作时,序列的内容就会一直在1、3、5、7、9只见循环出现。
DROP SEQUENCE myseq;
CREATE SEQUENCE myseq
START WITH 1
INCREMENT BY 2
MAXVALUE 10
MINVALUE 1
CYCLE
CACHE 3;
SELECT * FROM User_Sequences;
SELECT myseq.nextval FROM dual;
SELECT myseq.currval FROM dual;
/*
9.2.4修改序列
虽然Oracle提供了序列的修改语句,但序列功能主要是完成自动增长列的操作,不建议修改序列。
不能修改start with属性
*/
DROP SEQUENCE myseq;
CREATE SEQUENCE myseq;
SELECT * FROM user_sequences;
ALTER SEQUENCE myseq
INCREMENT BY 10
MAXVALUE 89765
CACHE 100;
SELECT * FROM User_Sequences;
/*
9.2.5 自动序列,从oracle 12c起,为了方便用户自动生成流水号提供了自动增长列。
*/
--定义自动增长列语法:
CREATE TABLE 表名(
列名 类型 GENERATED BY DEFAULT AS IDENTITY([INCREMENT BY 步长]
[START WITH 开始值]
[MAXVALUE 最大值 | NOMAXVALUE]
[MINVALUE 最小值 | NOMINVALUE]
[CYCLE | NOCYCLE]
[CACHE 缓存大小 | NOCACHE]),
列名 类型,
...
);
--实例:有报错,可以使用触发器实现插入数据主键递增
DROP TABLE member_Test;
CREATE TABLE member_test(
mid NUMBER GENERATED BY DEFAULT AS IDENTITY( START WITH 1
INCREMENT BY 1),
mname VARCHAR(40) DEFAULT 'LH',
sex VARCHAR(10),
CONSTRAINT pk_mid PRIMARY KEY(mid),
CONSTRAINT ck_sex CHECK(sex IN('男', '女'))
);
/*
1、序列号的特点:
1)它是一个可以由多个用户共享的数据库对象;
2)它是一个序列号产生器,专为表中的数据行自动产生序列号;
3)序列号是由Oracle内部集成产生和维护的;
4)它独立于使用它的表;
5)它通常是用来产生主键(唯一的号码);
6)它可以取代差生序列号的应用程序;
7)如果让它常驻内存,可提高访问序列号的效率。
使用序列号
1、sequence.NEXTVAL:返回序列号sequence的下一个可获得的值
2、sequence.CURRVAL:返回序列号sequence的当前值。
Oracle规定:在引用CURRVAL之前,必须在当前的会话中(账号下)使用NEXTVAL产生一个序列号的值。
3、使用CURRVAL和NEXTVAL的情况:
1)在查询语句的SELECT子句中,但不包括子查询中的SELECT子句;
2)在UPDATE语句的SET子句中,但不能在SET子句的子查询中;
3)在INSERT语句中的子查询的SELECT列表中;
4)在INSERT语句的VALUES子句中;
4、不能使用CURRVAL和NEXTVAL的情况:
1)视图的SELECT子句中
2)UPDATE语句的子查询中
3)DELETE语句的子查询中
4)在包含DISTINCT关键字的查询语句中
5)在包含ORDER BY子句的查询语句中
6)在包含GROUP BY子句的查询语句中
7)在包含HAVING子句的查询语句中
8)在包含DEFAULT关键字的CREATE TABLE语句中
9)在包含DEFAULT关键字的ALTERTABLE语句中
5、使用情况:
在对数据连续没有要求的情况下,可以设置CACHE加快获取速度;
如果要求数据连续,最好设置NOCACHE,但是仍然避免不了序列号间隔问题;
6、造成序列号码间隔(序列号码丢失)的情况:
当使用该序列号的事物被回滚;
当该序列号还用于另外的表时;
当系统崩溃时。
*/
/*
-----9.3:同义词
为一个数据库对象起另一个名
*/
--当前用户执行获取系统时间,sys用户从tab中获取表名为dual的信息可知,当前用户获取dual表信息应该是sys.dual,
--此处就把dual定义为sys.dual的同义词了
SELECT SYSDATE FROM dual;
SELECT * FROM tab WHERE tname = 'dual';
/*
语法:必须使用管理员用户登录才能创建同义词,或是具有创建同义词的相关权限用户
同义词本身也是一个数据库对象,可以通过user_synonyms数据字典表查看
*/
CREATE [PUBLIC] SYNONYM 同义词名 FOR 数据库对象;
--范例:此时只能被创建用户所使用(非public)
conn SYS/dccplh AS SYSDBA;
CREATE SYNONYM mystock FOR jssc.stock;
SELECT * FROM mystock;
SELECT * FROM User_Synonyms;
DROP SYNONYM mystock;
--创建公共同义词mystock2(public删除时也要加上此参数)
DROP PUBLIC SYNONYM mystock2;
CREATE PUBLIC SYNONYM mystock2 FOR jssc.stock;
SELECT * FROM User_Synonyms WHERE synonym_name = 'mystock2';
SELECT * FROM mystock2;
/*
-----9.4 Oracle伪列
Oracle数据库中为了实现完整的关系数据库功能,提供了许多伪列,前面的序列中nextval、currval,还有sysdate、
systimestamp也属于伪列(查询的dual表为伪表),本节介绍另两个伪列:rownum和rowid
*/
/*
9.4.1 ROWID伪列
数据表中每一行记录都有一个唯一的地址编号,所有的数据可通过rowid进行定位
rowid的操作函数
通过使用数据对象号,Oracle服务器可以找到包含数据行的表空间,之后使用表空间中的相对文件号可以确定文件,再利用
数据块号可以确定包含所需数据行的数据块,最后使用行号可以确定数据行目录项。
*/
SELECT ROWID, deptno, dname, loc FROM dept;
SELECT ROWID, dbms_rowid.rowid_object(ROWID) 数据对象号,
dbms_rowid.rowid_relative_fno(ROWID) 相对文件号,
dbms_rowid.rowid_block_number(ROWID) 数据块号,
dbms_rowid.rowid_row_number(ROWID) 数据行号
FROM emp;
--应用场景:若某表数据有重复的数据,要求删除重复数据只留一条:如dept表有3个10部门信息,要求只保留一个10部门信息
--对dept分组,统计出唯一的rowid数据,这个查询结果正是最后删除重复数据后应有内容
SELECT deptno, dname, loc, MIN(ROWID) FROM dept GROUP BY deptno, dname, loc;
DELETE FROM dept
WHERE ROWID NOT IN (SELECT deptno, dname, loc, MIN(ROWID)
FROM dept
GROUP BY deptno, dname, loc);
/*
9.4.2 rownum伪列
表示的是一个数据行编号的伪列,内容是在用户查询数据时,为用户动态分配的一个数字(行号)
rownum数据是随机生成的,并不和某一行数据永久绑定。
*/
SELECT ROWNUM, stkid FROM stock;
SELECT * FROM stock;
--应用场景:数据的分页显示,当数据较多时,不能用select * from,可以考虑分页查询浏览信息。
--1、获取dept表前五条信息(子查询中结果也对,但不是标准格式):
SELECT *
FROM (SELECT empno, ename, job, hiredate, sal, mgr, deptno, rownum rn
FROM emp
WHERE rownum <= 5) temp
WHERE temp.rn > 0;
--2、获取雇员表6~10条记录
SELECT *
FROM (SELECT empno, ename, job, hiredate, sal, mgr, deptno, rownum rn
FROM emp
WHERE rownum <= 10) temp
WHERE temp.rn > 5;
/*
-----9.4.3 Oracle新特性FETCH
Oracle 12c中为了方便数据的分页显示,专门提供了FETCH语句,使用此语句可以方便的取得指定范围内操作数据
*/
/*
语法:FETCH语句是放在整体查询语句的最后位置,该语句有3种使用方式:
1、取得前N行数据:FETCH FIRSH 行数 ROW ONLY
2、取得指定范围记录:OFFSET 开始位置 ROWS FETCH NEXT 个数 ROWS ONLY
3、按照百分比取记录:FETCH NEXT 百分比 PERCENT ROWS ONLY
*/
SELECT [DISTINCT] 分组字段1 AS 列别名, [分组字段2 AS 列别名]
FROM 表1 表1别名, 表2 表2别名
[WHERE 条件s]
[GROUP BY 分组字段1, 分组字段2...]
[HAVING 过滤条件s]
[ORDER BY 排序字段 ASC | DESC]
[FETCH FIRST 行数] | [OFFSET 开始位置 ROWS FETCH NEXT 个数] | [FETCH NEXT 百分比 PERCENT] ROW ONLY
--范例1:取得emp表前5行数据,只适用于oracle 12c版
SELECT * FROM emp FETCH FIRST 5 ROW ONLY;
SELECT * FROM emp ORDER BY sal DESC FETCH FIRST 5 ROW ONLY;
--范例2:取得表中4~5条记录,只适用于oracle 12c版
SELECT * FROM emp ORDER BY sal DESC offset 3 rows FETCH NEXT 2 rows ONLY;
--范例3:按百分比取部分数据
SELECT * FROM emp ORDER BY sal DESC FETCH NEXT 10 percent rows ONLY;
--上述分页操作只适用于oracle12c版本,习惯性做法还是利用rownum嵌套子查询的方式完成分页数据获取。
/*
-----9.5 索引
B*Tree索引、降序索引、位图索引、函数索引
*/
/*
9.5.1 B*Tree索引
一般B树索引在检索高基数数列(该列上的重复内容较少或没有)的时候可以提高性能的检索操作。
*/
/*
范例1、假定sal字段非重复,下述查询会进行全表扫描,将符合数据返回显示。
确定是否全表扫描:用sys用户登录,使用set autotrace on;打开跟踪步骤:
conn sys/change_on_install as sysdba;
set autotrace on;
SELECT * FROM emp WHERE sal > 1500;
出现table access full(全表扫描)提示
利用rowid查找是最高效做法
*/
SELECT * FROM emp WHERE sal > 1500;
/*
在Oracle如果想要创建B*Tree索引,有以下方式:
1、当某一个列设置了主键约束或唯一约束,则会自动创建索引。
2、利用命令直接创建索引。
*/
--此格式中,需要设置索引具体表的具体列,指定列的时候也可以设置升序或降序,默认值升序,降序则为降序索引
CREATE INDEX [用户名.]索引名 ON [用户名.]表名 (列名 [ASC | desc],...);
--范例:emp.sal字段创建emp_sal_ind索引,创建完后使用同样的查询,观察跟踪工具结果:table access by index rowid batched
CREATE INDEX emp_sal_ind ON emp(sal);
SELECT * FROM User_Indexes;
/*
索引对性能的提升也不是一定的。在任何IT项目里存在两种模式,即以时间换空间、以空间换时间。以时间换空间是某些
很复杂的运算,利用计算时间加长的方式解决服务器资源不够问题;而以空间换时间指的是利用多台服务器,一起进行处理
这样速度就会提升,为解决中小型企业服务器不足问题,提出了云服务概念。
在更新频繁的数据表上使用索引反而会降低性能,最方便做法就是准备两张表,A表进行数据的更新,另一张表B在每天夜深人静
时将A表更新后数据保存在B表中,同时在B表设置索引,用户检索利用B表,更新使用A表,这样做可以提升性能,但会牺牲实时性,
这也就是以时间换空间。
*/
--范例:在hiredate字段设置降序索引
CREATE INDEX emp_hiredate_ind_desc ON emp(hiredate);
SELECT *
FROM emp
WHERE hiredate BETWEEN to_date('1988-02-13', 'yyyy-mm-dd') AND
to_date('2001-09-23', 'yyyy-mm-dd')
ORDER BY hiredate DESC;
--创建函数索引:此时创建的索引在ename字段上使用lower函数时起作用。
CREATE INDEX emp_ename_ind ON emp(LOWER(ename));
SELECT * FROM emp WHERE LOWER(ename) = 'scott';
/*
位图索引:适用于列上数据属于低基数的时候。
*/
/*
如雇员表中deptno会重复多次,取值有限,如果雇员很多,按部门编号查找默认会全表扫描。如果deptno只有10、20、30三个
基数值,但emp表却又30万行数据,按deptno进行分类只有三组,进行位图查找时候非常方便快捷。
*/
CREATE BITMAP INDEX [用户名.]索引名 ON [用户名.]表名 (列名 [ASC | DESC]);
CREATE BITMAP INDEX emp_deptno_ind ON emp(deptno);
SELECT * FROM emp WHERE deptno = 10;
SELECT * FROM user_indexes;
--删除索引:删除后,其对应的内存空间也一同被释放
DROP INDEX 索引名;
/*
本章小结:
1、用户通过视图可以实现复杂SQL语句封装,为开发人员提供便利。
2、视图本身不属于真实数据,所以建议在创建视图时利用with read only设置为只读视图。
3、用户通过序列可以实现数据的自动增长,主要使用nextval伪列操作。
4、dual虚拟表实际是sys.dual的同义词,如果用户定义的同义词希望被多个用户访问,定义时使用public。
5、rownum可以在数据查询时自动生成行号,开发中主要可以用其实现数据的分页显示操作。
6、每一行数据都会存在唯一的物理地址(rowid)。
7、索引是提升数据库查询性能的手打un,但是在频繁更新时,反而会造成性能的降低。
*/
/*==========================================第十章 用户权限及角色管理 ============================================================================*/
/*
-----10.1 用户管理
*/
/*
创建用户:
1、DEFAULT TABLESPACE mldn_data :用户默认使用表空间,如果用户在创建数据库对象时没有指定表空间,默认
使用mldn_data表空间存储。
2、TEMPORARY TABLESPACE mldn_temp:该用户使用order by或group by子句数据了过大时保存的临时表空间。
3、QUOTA 30m ON mldn_data:该用户在mldn_data表空间最多使用30MB大小。
4、QUOTA 20m ON userss:该用户在userss表空间最多使用20MB大小。
5、ACCOUNT UNLOCK:该用户默认为活动账户。
6、PASSWORD EXPIRE:用户首次登录后需要强制用户修改密码。
*/
CREATE USER mldnuser IDENTIFIED BY java_android
DEFAULT TABLESPACE mldn_data
TEMPORARY TABLESPACE mldn_temp
QUOTA 30m ON mldn_data
QUOTA 20m ON userss
ACCOUNT UNLOCK
PASSWORD EXPIRE;
--用户创建成功后可以查看dba_users数据字典
SELECT * FROM Dba_Users;
--通过dba_ts_quotas数据字典查看用户可用表空间配合
SELECT * FROM Dba_Ts_Quotas;
SELECT * FROM Dba_Profiles;
/*
10.2概要文件profiles
概要文件是一组命名了的口令和资源闲置文件,管理员利用它可以直接限制用户的资源访问量或用户管理操作。
定义、查询、配置、修改、删除概要文件略。
*/
/*
-----10.3维护用户
*/
--1、修改用户密码
ALTER USER 用户名 IDENTIFIED BY 新密码;
--2、控制用户锁定
ALTER USER 用户名 ACCOUNT LOCK | UNLOCK;
--通过dba_users数据字典查看用户锁定状态
SELECT * FROM dba_users;
--3、让密码失效:密码失效之后用户必须在进行登录时强制性修改密码
ALTER USER 用户名 PASSWORD EXPIRE;
--4、修改用户表空间配额
ALTER USER 用户名 QUOTA 数字 [K | M] UNLIMITED ON 表空间名...
ALTER USER c##mldnuser
QUOTA 20M ON SYSTEM
QUOTA 30M ON USERS;
SELECT * FROM Dba_Ts_Quotas;
/*
-----删除用户
如果用户存在期间进行了数据库对象创建,则可以利用cascade子句删除模式中所有对象。
*/
DROP USER 用户名 [CASCADE];
/*
=====10.4 权限管理
系统权限:进行数据库资源操作的权限,如创建表、索引等。
对象权限:维护数据库中对象的能力,即由一个用户操作另一个用户的对象。
所有的权限应该由DBA控制,两个核心命令:GRANT 和 REVOKE
*/
--1、系统权限
GRANT CREATE SESSION TO c##mldnuser;
--下面将3种对象的创建权限授予了c##mldnuser用户,授权同时使用了with admin option,表示用户可以将其权限授予其他用户
GRANT CREATE TABLE, CREATE VIEW, CREATE SEQUENCE TO c##mldnuser WITH ADMIN OPTION;
--利用c##mldnuser登录再执行下面语句
GRANT CREATE TABLE, CREATE SEQUENCE TO c##mldnjava;
--通过查看dba_sys_privs数据字典了解用户权限
SELECT *
FROM dba_sys_privs
WHERE grantee IN ('c##mldnjava', 'c##mldnuser')
ORDER BY grantee DESC;
--撤销权限 :c##mldnuser的create table,create view权限被回收,但通过它给c##mldnjava赋予的权限没被回收
REVOKE CREATE TABLE, CREATE VIEW FROM c##mldnuser;
/*
2、对象权限
Oracle一共定义了8种对象权限:
SELECT INSERT UPDATE DELETE EXECUTE ALTER INDEX REFERENCES
*/
--通过c##mldnuser无法访问c##scott用户下的资源
SELECT * FROM c##scott.dept;
--语法:
GRANT 对象权限 | ALL[(列, ...)]
ON 对象
TO [用户名 | 角色名 | PUBLIC]
[WITH GRANT OPTION];
--为c##mldnuser用户授予c##scott用户dept表的查询及增加权限
GRANT SELECT, INSERT ON c##scott.dept TO c##mldnuser;
--将c##scott.dept数据表更新部门名称(dname)的权限授予c##mldnuser用户
GRANT UPDATE(dname) ON c##scott.dept TO c##mldnuser;
--所设置的对象权限,可以通过登录用户的user_tab_privs_recd数据字典查看
SELECT * FROM User_Tab_Privs_Recd;
--回收权限:只能按照对象权限回收,不能按照列权限回收,如update(dname),回收权限时只能从整个表回收权限。
REVOKE SELECT, INSERT ON c##scott.dept FROM c##mldnuser;
REVOKE UPDATE ON c##scott.dept FROM c##mldnuser;
/*
=====10.5角色
就是一组相关权限的集合,通过对角色的维护来实现对多个用户的权限维护。
管理员可以通过3个数据字典查看用户、角色、权限信息
dba_sys_privs:查看用户所拥有的权限
dba_role_privs:查看用户所拥有的角色
role_sys_privs:查看角色拥有权限
*/
--10.5.1创建角色
CREATE ROLE 角色名 [NOT IDENTIFIED | IDENTIFIED BY 密码];
CREATE ROLE c##mldn_role_a; --默认无密码
CREATE ROLE c##mldn_role_b IDENTIFIED BY hellojava;
--关于角色的密码(了解即可)
SET ROLE NONE;
SET ROLE ALL;
SET ROLE c##mldn_role_b IDENTIFIED BY hellojava;
SELECT * FROM dba_roles WHERE ROLE IN('c##mldn_role_a', 'c##mldn_role_b');
-----10.5.2角色授权
GRANT CREATE SESSION, CREATE TABLE, CREATE VIEW, CREATE SEQUENCE TO c##mldn_role_a;
GRANT CREATE SESSION, CREATE ANY TABLE, INSERT ANY TABLE TO c##mldn_role_b;
SELECT * FROM Role_Sys_Privs WHERE ROLE IN ('C##MLDN_ROLE_A', 'C##MLDN_ROLE_B');
-----10.5.3为用户授予角色
GRANT c##mldn_role_a TO c##mldnuser;
GRANT c##mldn_role_a, c##mldn_role_b TO c##mldnjava;
SELECT * FROM Session_Privs;
-----10.5.4修改角色及回收角色权限
--1、角色密码设置
ALTER ROLE c##mldn_role_a IDENTIFIED BY hellomldn;
ALTER ROLE c##mldn_role_b NOT IDENTIFIED;
--2、通过角色回收权限
REVOKE CREATE SESSION FROM c##mldn_role_a;
--删除角色:角色被删除后,其拥有此角色的用户权限也将一起被删除
DROP ROLE c##mldn_role_a;