单行函数主要分为以下五类:字符函数、数字函数、日期函数、转换函数、通用函数;
一、字符函数
字符函数的功能主要是进行字符串数据的操作,下面给出几个字符函数:
- UPPER(字符串 | 列):将输入的字符串变为大写返回;
- LOWER(字符串 | 列):将输入的字符串变为小写返回;
- INITCAP(字符串 | 列):开头首字母大写;
- LENGTH(字符串 | 列):求出字符串的长度;
- REPLACE(字符串 | 列):进行替换;
- SUBSTR(字符串 | 列,开始点 [,结束点]):字符串截取;
Oracle之中有一点比较麻烦,即使要验证字符串,也必须编写完整的SQL语句,所以在Oracle数据库之中为了用户查询方便,所以专门提供了一个“dual”的虚拟表。
范例:观察转大写的函数
SELECT UPPER('hello') FROM dual;
SQL> SELECT UPPER('hello') FROM dual; UPPER ----- HELLO
大写转换的用处:在一般的使用之中,用户输入数据的时候去关心数据本身存放的是大写还小写吗?
SELECT * FROM emp WHERE ename='&str';
SQL> SELECT * FROM emp WHERE ename='&str'; 输入 str 的值: smith 原值 1: SELECT * FROM emp WHERE ename='&str' 新值 1: SELECT * FROM emp WHERE ename='smith' 未选定行 SQL> SELECT * FROM emp WHERE ename='&str'; 输入 str 的值: SMITH 原值 1: SELECT * FROM emp WHERE ename='&str' 新值 1: SELECT * FROM emp WHERE ename='SMITH' EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- 7369 SMITH CLERK 7902 17-12月-80 800 20
此时如果输入的是小写,则肯定无法查询出数据,所以这个时候不能要求用户这么多,所以这个时候只能由程序自己去适应,加入一个函数:
SELECT * FROM emp WHERE ename=UPPER('&str');
SQL> SELECT * FROM emp WHERE ename=UPPER('&str'); 输入 str 的值: smith 原值 1: SELECT * FROM emp WHERE ename=UPPER('&str') 新值 1: SELECT * FROM emp WHERE ename=UPPER('smith') EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- 7369 SMITH CLERK 7902 17-12月-80 800 20
当然以上的“&”的操作属于替代变量的内容,此部分内容不做重点。
范例:观察转小写的操作,将所有的雇员姓名按照小写字母返回
SELECT LOWER(ename) FROM emp;
范例:将每一个雇员姓名的开头首字母大写
SELECT INITCAP(ename) FROM emp;
范例:查询出每个雇员姓名的长度
SELECT ename,LENGTH(ename) FROM emp;
范例:要求查询出姓名长度正好是5的雇员信息
SELECT ename,LENGTH(ename) FROM emp WHERE LENGTH(ename)=5;
范例:使用字母“_”替换掉姓名中的所有字母“A”
SELECT REPLACE(ename,'A','_') FROM emp;
字符串截取操作有两种语法:
语法一:SUBSTR(字符串 | 列,开始点),表示从开始点一直截取到结尾;
SELECT ename,SUBSTR(ename,3) FROM emp;
语法二:SUBSTR(字符串 | 列,开始点,结束点),表示从开始点截取到结束点,截取部分内容;
SELECT ename,SUBSTR(ename,0,3) FROM emp; SELECT ename,SUBSTR(ename,1,3) FROM emp;
范例:要求截取每个雇员姓名的后三个字母
- 正常思路:通过长度-2确定开始点
SELECT ename,SUBSTR(ename,LENGTH(ename)-2) FROM emp;
- 新思路:设置负数,表示从后指定截取位置;
SELECT ename,SUBSTR(ename,-3) FROM emp;
面试题:请问SUBSTR()函数截取的时候下标从0还是从1开始?
- 在Oracle数据库之中,SUBSTR()函数从0或1开始都是一样的;
- SUBSTR()也可以设置为负数,表示由后指定截取的开始点;
二、数字函数
数字函数一共有三个:
- ROUND(数字 | 列 [,保留小数的位数]):四舍五入的操作;
- TRUNC(数字 | 列 [,保留小数的位数]):舍弃指定位置的内容;
- MOD(数字 1,数字2):取模,取余数;
范例:验证ROUND()函数
SELECT ROUND(903.53567),ROUND(-903.53567), ROUND(903.53567,2), ROUND(-90353567,-1) FROM dual;
ROUND(903.53567) ROUND(-903.53567) ROUND(903.53567,2) ROUND(-90353567,-1) ---------------- ----------------- ------------------ ------------------- 904 -904 903.54 -90353570
范例:验证TRUNC()函数
SELECT TRUNC(903.53567),TRUNC(-903.53567), TRUNC(903.53567,2), TRUNC(-90353567,-1) FROM dual;
TRUNC(903.53567) TRUNC(-903.53567) TRUNC(903.53567,2) TRUNC(-90353567,-1) ---------------- ----------------- ------------------ ------------------- 903 -903 903.53 -90353560
范例:取模操作
SELECT MOD(10,3) FROM dual;
MOD(10,3) ---------- 1
以上的三个主要的数学函数,在学习Java中也会有相匹配的内容。
三、日期函数
如果现在要想进行日期的操作,则首先有一个必须要解决的问题,就是如何取得当前的日期,这个当前日期可以使用“SYSDATE”取得,代码如下:
SELECT SYSDATE FROM dual;
除了以上的当前日期之外,在日期中也可以进行若干计算:
- 日期 + 数字 = 日期,表示若干天之后的日期;
SELECT SYSDATE + 3,SYSDATE + 300 FROM dual;
- 日期 – 数字 = 日期,表示若干天前的日期;
SELECT SYSDATE - 3,SYSDATE - 300 FROM dual;
- 日期 – 日期 = 数字,表示的是两个日期间的天数,但是肯定是大日期 – 小日期;
范例:求出每个雇员到今天为止的雇佣天数
SELECT ename,hiredate,SYSDATE-hiredate FROM emp;
而且很多的编程语言之中,也都会提出一种概念,日期可以通过数字表示出来。
除了以上的三个公式之外,也提供了如下的四个操作函数:
- LAST_DAY(日期):求出指定日期的最后一天;
范例:求出本月的最后一天日期
SELECT LAST_DAY(SYSDATE) FROM dual;
- NEXT_DAY(日期,星期数):求出下一个指定星期X的日期;
范例:求出下一个周一
SELECT NEXT_DAY(SYSDATE,'星期一') FROM dual;
- ADD_MONTHS(日期,数字):求出若干月之后的日期;
范例:求出四个月后的日期
SELECT ADD_MONTHS(SYSDATE,4) FROM dual;
- MONTHS_BETWEEN(日期1,日期2):求出两个日期之间所经历的月份;
范例:求出每个雇员到今天为止的雇佣月份
SELECT ename,hiredate,TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)) FROM emp;
在所有的开发之中,如果是日期的操作,建议使用以上的函数,因为这些函数可以避免闰年的问题。
四、转换函数
现在已经接触到了Oracle数据库之中的三种数据:数字(NUMBER)、字符串(VARCHAR2)、日期(DATE),转换函数的主要功能是完成这几种数据间的互相转换操作,一共有三种转换函数:
- TO_CHAR(字符串 | 列,格式字符串):将日期或者是数字变为字符串显示;
- TO_DATE(字符串,格式字符串):将字符串变为DATE数据显示;
- TO_NUMBER(字符串):将字符串变为数字显示;
a、TO_CHAR()函数
在之前查询过当前的系统日期时间:
SELECT SYSDATE FROM dual;
这个时候是按照“日-月-年”的格式显示,很明显这种显示格式不符合正常的思路,正常是“年-月-日”,所以这种情况下可以使用TO_CHAR()函数,但是使用此函数的话需要一些格式字符串:年(yyyy),月(mm),日(dd)。
SELECT TO_CHAR(SYSDATE,'yyyy-mm-dd'),TO_CHAR(SYSDATE,'yyyy') year, TO_CHAR(SYSDATE,'mm') month, TO_CHAR(SYSDATE,'dd') day FROM dual;
TO_CHAR(SY YEAR MO DA ---------- ---- -- -- 2012-08-12 2012 08 12
但是这个时候的显示数据之中可以发现会存在前导0,如果要想消除掉这个0的话,可以加入一个“fm”。
SELECT TO_CHAR(SYSDATE,'fmyyyy-mm-dd') day FROM dual;
DAY----------2012-8-12
正常人都加0,所以这个标记知道就行了,可是在Oracle之中,DATE里面是包含了时间的,但是之前的代码没有显示出时间,要想显示时间则需要增加标记:
SELECT TO_CHAR(SYSDATE,'fmyyyy-mm-dd hh24:mi:ss') day FROM dual;
DAY ------------------- 2012-8-12 16:13:38
一定要注意,使用TO_CHAR()函数之后,所有的内容都是字符串,不再是之前的DATE型数据,TO_CHAR()函数也可以用于数字的格式化上,这个时候每一个“9”表示一位数字的概念,而不是数字9的概念。
SELECT TO_CHAR(89078907890,'L999,999,999,999,999') FROM dual;
TO_CHAR(89078907890,'L999,999, ------------------------------ ¥89,078,907,890
其中的字母“L”,表示的是“Local”的含义,即:当前的所在的语言环境下的货币符号。
b、TO_DATE()函数
此函数的主要功能是将一个字符串变为DATE型数据。
SELECT TO_DATE('1989-09-12','yyyy-mm-dd') FROM dual;
TO_DATE('1989- -------------- 12-9月 -89
一般此函数在更新数据库的时候使用较多;
c、TO_NUMBER()函数:基本不用
TO_NUMBER()函数一看就知道是将字符串变数字的:
SELECT TO_NUMBER('1') + TO_NUMBER('2') FROM dual;
但是在Oracle之中真的很智能,所以以上的功能不使用TO_NUMBER()也可完成:
SELECT '1' + '2' FROM dual;
SQL> SELECT TO_NUMBER('1') + TO_NUMBER('2') FROM dual; TO_NUMBER('1')+TO_NUMBER('2') ----------------------------- 3 SQL> SELECT '1' + '2' FROM dual; '1'+'2' ---------- 3
所以现在的TO_NUMBER()函数基本上已经是不考虑了,重点的函数在TO_CHAR()上,其次是TO_DATE()函数。
五、通用函数
通用函数主要有两个:NVL()、DECODE(),这两个函数算是Oracle自己的特色函数了;
a、NVL()函数,处理null
范例:要求查询出每个雇员的全部年薪
SELECT ename,sal,comm,(sal+comm)*12 FROM emp;
SQL> SELECT ename,sal,comm,(sal+comm)*12 FROM emp; ENAME SAL COMM (SAL+COMM)*12 ---------- ---------- ---------- ------------- SMITH 800 ALLEN 1600 300 22800 WARD 1250 500 21000 JONES 2975 MARTIN 1250 1400 31800 BLAKE 2850 CLARK 2450 SCOTT 800 KING 5000 TURNER 1500 0 18000 ADAMS 1100 JAMES 950 FORD 3000 MILLER 1300 已选择14行。
这个时候有的雇员的年薪就变成了null,而造成这种问题的关键是在于comm字段上为null,那么要想解决这个问题,就必须做一种处理:将null变为0,而这个就是NVL()函数的作用。
SELECT ename,sal,comm,(sal+NVL(comm,0))*12,NVL(comm,0) FROM emp;
SQL> SELECT ename,sal,comm,(sal+NVL(comm,0))*12,NVL(comm,0) FROM emp; ENAME SAL COMM (SAL+NVL(COMM,0))*12 NVL(COMM,0) ---------- ---------- ---------- -------------------- ----------- SMITH 800 9600 0 ALLEN 1600 300 22800 300 WARD 1250 500 21000 500 JONES 2975 35700 0 MARTIN 1250 1400 31800 1400 BLAKE 2850 34200 0 CLARK 2450 29400 0 SCOTT 800 9600 0 KING 5000 60000 0 TURNER 1500 0 18000 0 ADAMS 1100 13200 0 JAMES 950 11400 0 FORD 3000 36000 0 MILLER 1300 15600 0 已选择14行。
b、DECODE()函数:多数值判断
DECODE()函数非常类似于程序中的if…else…语句,唯一不同的是DECODE()函数判断的是数值,而不是逻辑条件。
例如,现在要求显示全部雇员的职位,但是这些职位要求替换为中文显示:
- CLERK:办事员;
- SALESMAN:销售;
- MANAGER:经理;
- ANALYST:分析员;
- PRESIDENT:总裁;
这种判断肯定是逐行进行判断,所以这个时候就必须采用DECODE(),而此函数的语法如下:
DECODE(数值 | 列 ,判断值1,显示值1,判断值2,显示值2,判断值3,显示值3,…)
范例:实现显示的操作功能
SELECT empno,ename,job,DECODE(job,'CLERK','办事员','SALESMAN','销售人员','MANAGER','经理','ANALYST','分析员','PRESIDENT','总裁') FROM emp;
SQL> SELECT empno,ename,job,DECODE(job,'CLERK','办事员','SALESMAN','销售人员','MANAGER','经理','ANALYST','分析员','PRESIDENT','总裁') FROM emp; EMPNO ENAME JOB DECODE(J ---------- ---------- --------- -------- 7369 SMITH CLERK 办事员 7499 ALLEN SALESMAN 销售人员 7521 WARD SALESMAN 销售人员 7566 JONES MANAGER 经理 7654 MARTIN SALESMAN 销售人员 7698 BLAKE MANAGER 经理 7782 CLARK MANAGER 经理 7788 SCOTT CLERK 办事员 7839 KING PRESIDENT 总裁 7844 TURNER SALESMAN 销售人员 7876 ADAMS CLERK 办事员 7900 JAMES CLERK 办事员 7902 FORD ANALYST 分析员 7934 MILLER CLERK 办事员 已选择14行。
DECODE()函数是整个Oracle之中最具特点的函数,一定要将其掌握。
习题讲解
1、 选择部门30中的所有员工。
SELECT * FROM emp WHERE deptno=30;
2、 列出所有办事员(CLERK)的姓名,编号和部门编号。
SELECT empno, ename, deptno FROM emp WHERE job='CLERK';
3、 找出佣金高于薪金的员工。
SELECT * FROM emp WHERE comm>sal;
4、 找出佣金高于薪金的60%的员工。
SELECT * FROM emp WHERE comm>sal*0.6;
5、 找出部门10中所有经理(MANAGER)和部门20中所有办事员(CLERK)的详细资料。
SELECT * FROM emp WHERE (job='MANAGER' AND deptno=10) OR (job='CLERK' AND deptno=20);
6、 找出部门10中所有经理(MANAGER),部门20中所有办事员(CLERK),既不是经理又不是办事员但其薪金大于或等于2000的所有员工的详细资料。
SELECT * FROM emp WHERE (job='MANAGER' AND deptno=10) OR (job='CLERK' AND deptno=20) OR (job NOT IN ('MANAGER','CLERK') AND sal>=2000);
7、 找出收取佣金的员工的不同工作。
SELECT DISTINCT job FROM emp WHERE comm IS NOT NULL;
8、 找出不收取佣金或收取的佣金低于100的员工。
SELECT * FROM emp WHERE comm IS NULL OR comm<100;
9、 找出各月倒数第3天受雇的所有员工。
每一个雇员的雇佣日期肯定是不一样的,所以现在必须找到每一个雇员雇佣所在月的最后一天,之后按照“日期-数字”的方式求出前三天的日期,这个日期必须和雇佣日期相符合才满足条件。
SELECT * FROM emp WHERE LAST_DAY(hiredate)-2=hiredate;
10、 找出早于12年前受雇的员工。
如果要求年份,最准确的做法是使用总月数/12;
SELECT * FROM emp WHERE MONTHS_BETWEEN(SYSDATE,hiredate)/12>12;
11、 以首字母大写的方式显示所有员工的姓名。
SELECT INITCAP(ename) FROM emp;
12、 显示正好为5个字符的员工的姓名。
SELECT ename FROM emp WHERE LENGTH(ename)=5;
13、 显示不带有“R”的员工的姓名。
SELECT ename FROM emp WHERE ename NOT LIKE '%R%';
14、 显示所有员工姓名的前三个字符。
SELECT SUBSTR(ename,0,3) FROM emp;
15、 显示所有员工的姓名,用“a”替换所有“A”。
SELECT REPLACE(ename,'A','a') FROM emp;
16、 显示满10年服务年限的员工的姓名和受雇日期。
SELECT ename, hiredate FROM emp WHERE MONTHS_BETWEEN(SYSDATE,hiredate)/12>10;
17、 显示员工的详细资料,按姓名排序。
SELECT * FROM emp ORDER BY ename;
18、 显示员工的姓名和受雇日期,根据其服务年限,将最老的员工排在最前面。
SELECT ename, hiredate FROM emp ORDER BY hiredate;
19、 显示所有员工的姓名、工作和薪金,按工作的降序排序,若工作相同则按薪金排序。
SELECT ename, job, sal FROM emp ORDER BY job DESC,sal;
20、 显示所有员工姓名、加入公司的年份和月份,按受雇日期所有月排序,若月份相同则将最早年份的员工排在最前面。
本程序需要从日期之中取出年份和月份,用TO_CHAR()函数完成。
SELECT ename,TO_CHAR(hiredate,'yyyy') year,TO_CHAR(hiredate,'mm') months FROM emp ORDER BY months,year;
21、 显示在一个月为30天的情况所有员工的日薪金,忽略余数。
SELECT ename,sal,TRUNC(sal/30) FROM emp;
22、 找出在(任何年份的)2月受聘的所有员工。
SELECT * FROM emp WHERE TO_CHAR(hiredate,'mm')=2;
23、 对于每个员工,显示其加入公司的天数。
SELECT ename,SYSDATE-hiredate FROM emp;
24、 显示姓名字段的任何位置包含“A”的所有员工的姓名。
SELECT ename FROM emp WHERE ename LIKE '%A%';
25、 以年月日的方式显示所有员工的服务年限。
第一步:求出每个雇员的雇佣年数:被雇佣的总月数 / 12 = 年数;
SELECT ename, hiredate, TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) yearFROM emp;
第二步:求出月数,以上计算之中被忽略的小数点实际上都是月份,所以直接取余即可;
SELECT ename, hiredate, TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year, TRUNC(MOD(MONTHS_BETWEEN(SYSDATE,hiredate),12)) months FROM emp;
第三步:求出天数,最准确的做法是在不超过30天的范围之内求;
现在已经知道当前的时间使用SYSDATE取出,而雇佣的日期使用hiredate取出,可是hiredate和SYSDATE之间的差距太大了,所以肯定会有误差,那么就必须想办法将hiredate的日期提升到与SYSDATE差距在30天的范围之内。
在之前学习过两个函数:
- MONTHS_BETWEEN():求出两个日期间的月数,如果是:MONTHS_BETWEEN(SYSDATE,hiredate)求出的是雇佣日期到今天为止的雇佣月份;
- ADD_MONTHS():在一个日期上加入指定的月之后的日期,如果说hiredate + 与今天相距的月数 = 一个新的日期,而且这个新的日期肯定和SYSDATE相距不超过30天。
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))) dayFROM emp;