前言
该文章内容不是学习oracle的基础知识,而是在大致了解oracle的基础知识上看我自己还有什么不知道或者不明白的地方,有兴趣的小伙伴可以在网上找找李兴华的oracle培训资料。
SQL简介
SQL 功能强大,概括起来,它可以分成以下几组:
· DML(Data Manipulation Language,数据操作语言) —— 指的是实现数据库的查询与更新操作,其中针对于 数据库的查询有部分人员会将其称为 DQL,因为最麻烦的部分就是查询。
· DDL(Data Definition Language,数据定义语言) —— 定义数据库对象,例如:在一些系统设计人员的工作里 面需要进行数据表的定义。
· DCL(Data Control Language,数据控制语言) —— 控制用户的操作权限,一般由 DBA 负责。
简单查询
对于重复的数据是有一个基本的原则:只有在所有列的数据都重复的情况下才叫 做重复数据。
SELECT DISTINCT ename,job FROM emp
“||”的连接符,可以进行数据的连接。
SELECT '编号:' || empno || '、姓名:' || ename || '、工资:' || sal info FROM emp
IN查询
对于 IN 操作而言是针对于基数的判断,如果说此时在使用 NOT IN 的时候基数里面包含了 NULL,那么所有满足
的数据都不会被显示。
范例:观察程序,在 IN 之中使用 NULL —— 现在没有任何的影响
SELECT * FROM emp WHERE empno IN (7369,7566,7839,NULL) ;
范例:观察程序,在 NOT IN 之中使用 NULL
SELECT * FROM emp WHERE empno NOT IN (7369,7566,7839,NULL) ;
不满足的原因是数据库自身的保护机制,避免大数据所带来的问题
模糊查询
字段 LIKE 匹配关键字,而在进行关键字匹配的时候有两个符号标记:
|- “_”:匹配任意一个字符;
|- “%”:匹配 0 个、1 个或多个任意字符。
范例:查询所有姓名第二个字母以 A 开头的所有雇员
SELECT * FROM emp WHERE ename LIKE '_A%' ;
SQL执行顺序
自定义输入数据
要查询的数据表名称由用户输入 SELECT * FROM &tablename 利用替代变量输入雇员姓名 SELECT * FROM emp WHERE ename=UPPER('&name') ;
字符串函数
- UPPER()、LOWER()
-
首字母大写,其余字母小写:字符串 initcap(字符串 | 列)
将每一位雇员姓名的首字母大写,其余字母小写 SELECT INITCAP(ename) FROM emp ;
- 取得字符串长度:数字 LENGTH(字符串 | 列)
- 替换指定字符串的内容:字符串 REPLACE(字符串 | 列,要替换的内容,新的内容)
替换数据列 SELECT ename,REPLACE(ename,'A','_') FROM emp ;
- 字符串截取操作:
- 由指定位置截取到结尾:字符串 SUBSTR(字符串 | 数据列,截取开始索引);
- 指定截取的开始和结束位置:字符串 SUBSTR(字符串 | 数据列,截取开始索引,截取结束索引);
SELECT SUBSTR('hello world',0,5) FROM dual ; SELECT SUBSTR('hello world',1,5) FROM dual ; 以上俩个结果一致,实际上在 Oracle 之中,字符串的索引下标是从 1 开始的(程序是从 0 开始的),但是为了考虑到程序人员的使用习惯, 所以即使设置了 0,那么也表示从 1 开始,这一点会由 Oracle 自己进行处理。
-
去掉左右空格函数:字符串 TRIM(字符串 | 列)
截取每一位雇员姓名的后三位字母 SELECT ename,SUBSTR(ename,LENGTH(ename)-2) FROM emp ; SELECT ename,SUBSTR(ename,-3) FROM emp ;
数字函数
- 四舍五入函数:数字 ROUND(数字 | 列 [,保留小数位])
SELECT ROUND(9615.7652) , ➡️9616 ROUND(9615.7652,2), ➡️9615.77 ROUND(9615.7652,-2), ➡️9600 ROUND(-15.5) ➡️-16 FROM dual ;
- 截取小数函数:数字 TRUNC(数字 | 列 [,保留小数位])
SELECT ROUND(9615.7652) , ➡️9615 ROUND(9615.7652,2), ➡️9615.76 ROUND(9615.7652,-2), ➡️9600 ROUND(-15.5) ➡️-15 FROM dual ;
- 求模:数字 MOD(数字1| 列1,数字2| 列2)
求余数 SELECT MOD(10,3) FROM dual ;
日期函数
取得当前的系统日期时间,为此在 Oracle 里面提供有两个伪列(是不存在表中的列,但是却可以直接使用的列):SYSDATE、SYSTIMESTAMP
- 计算两个日期之间所经历的月数:数字 MONTHS_BETWEEN(日期 1 | 列 1,日期 2 | 列 2)
计算每一位雇员到今天为止雇佣的年数 SELECT ename,MONTHS_BETWEEN(SYSDATE,hiredate) , TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) FROM emp ;
- 加上指定月之后的日期:日期 ADD_MONTHS(日期 | 列 ,月数)
要求计算出每一位雇员到今天为止雇佣的年限(理解) A、 例如:今天是 2014 年 08 月 12 日,CLARK 的雇佣日期是:1981 年 06 月 09 日 B、 到今天 CLARK 已经在公司服务了:33 年、02 月、03 天 SELECT ename,hiredate, TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year , TRUNC(MOD(MONTHS_BETWEEN(SYSDATE,hiredate),12)) months , TRUNC(SYSDATE - ADD_MONTHS(hiredate,MONTHS_BETWEEN(SYSDATE,hiredate))) day FROM emp ;
- 求出指定的下一个一周时间数的日期:日期 NEXT_DAY(日期 | 列,一周时间数)
计算下一个星期二 SELECT NEXT_DAY(SYSDATE,'星期二') FROM dual ;
- 求出指定日期所在月的最后一天日期:日期 LAST_DAY(日期 | 列)
查询出在每个雇员雇佣所在月倒数第二天雇佣的全部雇员信息 SELECT ename,hiredate,LAST_DAY(hiredate)-2 FROM emp WHERE LAST_DAY(hiredate)-2=hiredate ;
转换函数
- 转字符串数据:字符串 TO_CHAR(数字 | 日期 | 列,转换格式)
拆分日期数据 SELECT TO_CHAR(SYSDATE,'yyyy') year , TO_CHAR(SYSDATE,'mm') months , TO_CHAR(SYSDATE,'dd') day FROM dual ;
- 转日期数据:日期 TO_DATE(字符串,转换格式)
-
转数字类型:数字 TO_NUMBER(字符串,转换格式)
通用函数
- 处理 null 数据:数字 NVL(列 | 数据,默认值)
在数据库之中,null 与任何的数据进行数 学计算其结果都是 null,那么如果遇到了 null 应该按照数字 0 来处理,所以就可以使用 NVL()函数。 SELECT ename,sal,comm,(sal+NVL(comm,0))*12 FROM emp ;
- 多数据判断:数据 DECODE(字段 | 数据,判断数据 1,显示数据 1,判断数据 2,显示数据 2,... [默认显示])
将所有雇员的职位数据变为中文 SELECT ename,job , DECODE(job,'CLERK','办事员','SALESMAN','销售','MANAGER','经理','ANALYST','分析员','PRESIDENT','总裁','未 处理')
多表查询
将 emp 表(14行数据)和 dept(4行数据) 表实现多表查询 SELECT * FROM emp ,dept ;
执行结果会发现,返回结果有56条数据,为什么呢?出现这样的结果主要是由于数据库的数学公式所导致的,这是两个集合的“积”,而这种积在数据库之中被称为“笛卡尔积”。
如何去消除掉 这样的“笛卡尔积”呢?唯一的方案就是使用关联字段。
不推荐使用的关联方式:
SELECT e.empno,e.ename,e.job,e.sal,d.dname,d.loc FROM emp e , dept d WHERE e.deptno=d.deptno ;
虽然消除掉了显示的笛卡尔积,但是整个运算执行过程非常的缓慢。
对于多表查询来讲其有两种表的连接方式:
- 内连接:也被称为等值连接,上面不推荐的查询属于内连接;
- 外连接:左外连接、右外连接、全外连接
- 左外连接:字段 = 字段(+),“(+)”在等号右边表示左外连接
SELECT * FROM emp e,dept d WHERE e.deptno=d.deptno(+) ;
-
- 右外连接:字段(+) = 字段,“(+)”在等号左边表示右外连接
SELECT * FROM emp e,dept d WHERE e.deptno(+)=d.deptno ;
SQL:1999 语法支持
SELECT * | 字段 [别名] FROM 表 [CROSS JOIN 表] [NATURAL JOIN 表] [JOIN] [USING(字段)] [ON(条件)] [LEFT | RIGTH | FULL | OUTER JOIN 表] ;
- 交叉连接,其主要的目的是为了产生笛卡尔积(不常用)
SELECT * | 字段 [别名] FROM 表 CROSS JOIN 表 ;
- 自然连接,主要是消除掉笛卡尔积(内连接)(不常用)
SELECT * | 字段 [别名] FROM 表 NATURAL JOIN 表 ;
- 使用 USING 子句,用户指定关联字段(不常用)
SELECT * | 字段 [别名] FROM 表 JOIN 表 USING(字段);
- 使用 ON 子句指定关联条件(不常用)
SELECT * | 字段 [别名] FROM 表 JOIN 表 ON(条件);
- 外连接(必须熟悉)
SELECT * | 字段 [别名] FROM 表 LEFT | RIGTH | FULL OUTER JOIN 表];
SELECT * FROM emp e LEFT OUTER JOIN dept d ON (e.deptno=d.deptno); SELECT * FROM emp e RIGHT OUTER JOIN dept d ON (e.deptno=d.deptno); SELECT * FROM emp e FULL OUTER JOIN dept d ON (e.deptno=d.deptno);
查询结果连接
SELECT [DISTINCT] * | 列名称 [别名] , 列名称 [别名] ,... FROM 数据表 [别名] , 数据表 [别名] ,... [WHERE 条件(s)] [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...] [UNION | UNION ALL | INTERSECT | MINUS] SELECT [DISTINCT] * | 列名称 [别名] , 列名称 [别名] ,... FROM 数据表 [别名] , 数据表 [别名] ,... [WHERE 条件(s)] [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...] [UNION | UNION ALL | INTERSECT | MINUS] SELECT [DISTINCT] * | 列名称 [别名] , 列名称 [别名] ,... FROM 数据表 [别名] , 数据表 [别名] ,... [WHERE 条件(s)] [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...]
- UNION 操作,不显示重复记录
- UNION ALL 操作,显示所有数据,包含重复数据
- INTERSECT 操作,返回相同的部分,交集
- MINUS 操作,返回不同的部分,是一个差集
组函数
统计函数常见的有五个:COUNT()(个数)、SUM()(求和)、AVG()(平均)、MAX()(最大值)、MIN()(最 小值)。
关于统计查询的限制:
- 限制一:统计函数单独使用时(没有 GROUP BY 子句)只能够出现统计函数,不能够出现其它字段。
- 限制二:使用统计查询时(存在 GROUP BY 子句),SELECT 子句之中只允许出现统计函数与分组字段, 其它的任何字段都不允许出现。
- 限制三:统计函数在分组之中可以嵌套使用,但是嵌套之后的统计查询之中,SELECT 子句不允许再出现 任何的字段,包括分组字段。
关于 WHERE 和 HAVING 的区别?
- WHERE 子句:是在分组前使用,而且不能够使用统计函数进行验证,经过 WHERE 筛选后的数据才可以分组;
- HAVING 子句:必须结合 GROUP BY 子句一起出现,是在分组后的过滤,可以使用统计函数。
子查询
- 在 WHERE 子句之中使用子查询
-
- IN:在指定范围内
- NOT IN:不在指定范围内
- ANY
- =ANY:此功能与 IN 操作完全一样
- >ANY:比子查询的最小值要大
- <ANY:比子查询的最大值要小
SELECT * FROM emp WHERE sal <ANY ( SELECT sal FROM emp WHERE job='MANAGER')
- ALL
- >ALL:比子查询返回的最大值还要大
- <ALL:比子查询最小值还要小
SELECT * FROM emp WHERE sal <ALL ( SELECT sal FROM emp WHERE job='MANAGER')
- 在 HAVING 子句之中使用子查询
SELECT deptno,AVG(sal) FROM emp GROUP BY deptno HAVING AVG(sal)>( SELECT AVG(sal) FROM emp)
- 在 FROM 子句之中使用子查询
SELECT d.deptno,d.dname,d.loc,temp.count,temp.avg FROM dept d , ( SELECT deptno ,COUNT(empno) count,AVG(sal) avg FROM emp GROUP BY deptno) temp WHERE d.deptno=temp.deptno(+) ;
问题: FROM 子句查询和多表查询有什么差异?emp 表之中存在有 14000 条数据,dept 表中存在有 4000 条数据,查找这两张表会有什么差异呢?
- 采用方式一的数据量,方式一使用的是多字段分组,那么就一定需要多表查询,多表查询一定会产生笛卡尔积;
|- 积的数量:emp 的 14000 条 * dept 的 4000 条 = 56,000,000 条数据
- 采用方式二的数据量,方式二使用的是子查询完成,需要分两步操作:
|- 第一步(内嵌子查询):针对于 emp 表查询,最多操作 14000 条记录,最多返回 4000 条记录; |- 第二步,子查询和 dept 表关联;
|- 积的数量:dept 表的 4000 条 * 子查询返回的最多 4000 条 = 16000000 条记录; |- 总的数据量:16000000 + 14000 = 16,014,000 条记录
主要目的:子查询的出现主要是为了解决多表查询之中的性能问题
次要目的:很多时候在 FROM 子句里面使用子查询,是因为在外部查询里无法再继续使用统计函数操作的时候
数据更新
复制表
CREATE TABLE myemp AS SELECT * FROM emp ;
数据删除
# 删除掉那些没有领导的雇员 DELETE FROM myemp WHERE mgr IS NULL ; # 所有员工删除 DELETE FROM myemp;
表的创建和管理
在 Oracle 之中数据表就被称为 数据库对象,而对 象 的 操作语法一共 有三种:
- 创建对象:CREATE 对象类型 对象名称 [选项];
- 删除对象:DROP 对象类型 对象名称 [选项];
- 修改对象:ALTER 对象类型 对象名称 [选项]。
常见的数据类型
- VARCHAR2:表示字符串,Oracle 的 VARCHAR2 类型可以保存 2000 个字符
- NUMBER:
NUMBER(n):表示不大于 n 位的整数,可以使用 INT 代替
NUMBER(n,m):表示有 m 个小数位和 n-m 个整数位,使用 FLOAT 代替
直接写 NUMBER 表示小数和整数都可以
- DATE:表示日期时间的数据,在 Oracle 中的 DATE 是存在有时间
- CLOB:大文本数据,可以保存 4G的文字数据
- BLOB:二进制数据,可以保存图片、音乐、电影、文字,最多能够保存 4G 的数据。 (少用)
重命名表名
RENAME旧的表名称 TO 新的表名称 ;
截断表
“DELETE FROM 表名称”这样的语 句完成,但是使用删除语句删除数据有一个问题:所有的数据被事务所管理,可以恢复,而且所有的数据删除之后其所占 用的一些资源(约束、索引等资源)并不能够立刻释放,那么如果希望表的所有资源被彻底释放,则只能够使用截断表的 操作,其操作语法如下:
TRUNCATE TABLE 表名称;
删除表
删除完成之后会遗留下一些内容
DROP TABLE 表名称;
修改表
# 为表中增加数据列 ALTER TABLE 表名称 ADD( 列名 数据类型 [DEFAULT 默认值], 列名 数据类型 [DEFAULT 默认值] ) # 修改列结构 ALTER TABLE 表名称 MODIFY( 列名 数据类型 [DEFAULT 默认值], 列名 数据类型 [DEFAULT 默认值] )