本篇文章比较长,请耐心查看,有问题可以在下面评论交流,喜欢博主的可以点个关注和收藏。
首先解决上篇文章遗留的问题
当某一列全空,对该列使用聚合函数会出现下面的情况
可能出现空值的聚合函数:MIN() MAX() SUM() AVG() 不可能出现空值:COUNT()
按转换的形式分为:
显式转换:数据类型的转换是用户主动执行转换函数而实现的
隐式转换:数据类型的转换是系统自动执行转换函数而实现的
SELECT * FROM EMP WHERE SAL = '3000'; -- 隐式转换
SELECT * FROM EMP WHERE SAL = TO_NUMBER('3000') -- 显式转换 后面会提到这个函数
无指定格式:单纯的由数值转换为字符
SELECT TO_CHAR(123) FROM DUAL; -- 字符型靠右边显式
有指定格式:将数值转换为字符的同时指定格式(精度 和 货币)
数值转字符中的通配符:通常用0或9来代指该位的内容
9:指任意数字,若整数位无数字则格式不生效,小数位无数字以0补全
0:指任意数字,若整数位无数字,则强制显示位0,小数位一般不用0代替
***注意
***:
转换过程中若遇小数位精度过低的情况,则会遵循四舍五入
遇整数位精度过低的情况,则会显示为#
可添加千分符
SELECT 123,TO_CHAR(123,'999.99') FROM DUAL; -- 结果123.00
SELECT 123,TO_CHAR(123,'999999.99') FROM DUAL; -- 结果123.00
SELECT 123,TO_CHAR(123,'00999.99') FROM DUAL; -- 结果00123.00
SELECT 123.456,TO_CHAR(123.456,'00999.00000') FROM DUAL; -- 结果00123.45600
SELECT 123.456,TO_CHAR(123.456,'99.9')FROM DUAL; --- 精度不够 显示 #####
SELECT TO_CHAR(123.45,'999.9') FROM DUAL; -- 结果 123.5
SELECT TO_CHAR(123456789.987,'99999,9999.999') FROM DUAL; -- 12345,6789.987
-- 进阶练习
SELECT TO_CHAR(999.99,'999.99') FROM DUAL; -- 999.99
SELECT TO_CHAR(999.99,'999.9') FROM DUAL; -- #### 1000.0 精度不够
SELECT TO_CHAR(999.99,'999,99') FROM DUAL; -- 10,00
SEELCT TO_CHAR(999.99,'999,9') FROM DUAL; -- 100,0
$:美元符号
L:当地货币
SELECT TO_CHAR(123.45,'$999.99') FROM DUAL; -- $123.45
SELECT TO_CHAR(123.45,'999.9$9') FROM DUAL; -- ERROR
SELECT TO_CHAR(123.45,'99$9$.99') FROM DUAL; -- ERROR
SELECT TO_CHAR(123.45,'L999.99') FROM DUAL; -- ¥123.45
SYSDATE:获取当前日期和时间
SYSTIMESTAMP:更详细的时间 毫秒级别
SELECT SYSDATE,SYSTIMESTAMP FROM DUAL; -- 获取当前日期
-- 2021/03/12 22:00:56 12-3月 -21 10.00.56.507000 下午 +08:00
日期格式的设置:固定字母或关键词来代指日期中的某一项内容,其中可以穿插一些分隔符号,且这些关键词可以随意搭配及更改顺序
常用
YYYY:四位年份
YYY:后三位年份 YY:后两位年份 Y:年份的最后一位
SELECT TO_CHAR(SYSDATE,'YYYY') FROM DUAL; -- 2021
SELECT TO_CHAR(SYSDATE,'YYYYYYY') FROM DUAL; -- 2021021 从前往后匹配四位,剩下的按照 YYY YY Y 显示
常用
Q:一年中的第几个季度(结果为一位)Q不区分大小写
SELECT TO_CHAR(SYSDATE,'Q') FROM DUAL; -- 返回当前月份所在的季度
MONTH:月份的全称(右侧空格补齐九位),针对英文来说
MON:三位字符的月份缩写,针对英文来说 [显示英文 需要加上 ‘NLS_DATE_LANGUAGE = AMERICAN’]
MM:一年中的第几个月(两位)
常用
SELECT TO_CHAR(SYSDATE,'MM') FROM DUAL; --- 03 月
SELECT TO_CHAR(SYSDATE,'MONTH','NLS_DATE_LANGUAGE = AMERICAN') FROM DUAL;
SELECT TO_CHAR(SYSDATE,'MON','NLS_DATE_LANGUAGE = AMERICAN') FROM DUAL;
一年中的第一天
开始算,一个七天作为一周,与星期几无关IW
:一年中的第几个星期,星期一作为本周的第一天。每年末最后一周不足四天,算到下一年的第一周
,足四天将下年本周的剩余时间一同算作本年最后一周
月
中的第几个星期,从第一天开始
算起,一个七天作为一周,和星期几无关1 WW
SELECT TO_CHAR(SYSDATE,'WW') FROM DUAL; -- 2021/03/13 一年中的第11周
2 IW
SELECT TO_CHAR(TO_DATE('20210101','YYYYMMDD'),'IW') FROM DUAL;-- 第53周 算到上一年的最后一周了
SELECT TO_CHAR(TO_DATE('20191230','YYYYMMDD'),'IW') FROM DUAL; -- 算到2020年第一周
3 W
SELECT TO_CHAR(SYSDATE,'W') FROM DUAL; -- 3月的第二个周
4 DAY
SELECT TO_CHAR(SYSDATE,'DAY','NLS_DATE_LANGUAGE=AMERICAN') FROM DUAL; -- SATURDAY 周六
5 DY
SELECT TO_CHAR(SYSDATE,'DY','NLS_DATE_LANGUAGE=AMERICAN') FROM DUAL; -- SAT 周六
D:星期中的第几天
从星期日开始算第一天
DD:月份中的第几天,两位
常用
DDD:月份中的第几天,三位
SELECT TO_CHAR(SYSDATE,'D') FROM DUAL; -- 2021/03/13 第七天
SELECT TO_CHAR(SYSDATE,'DD') FROM DUAL; -- 第13天
SELECT TO_CHAR(SYSDATE,'DDD') FROM DUAL; -- 一年中的第 072 天
HH, HH12:一天中的第几个小时(12小时制);如果区分上午下午 可以加上AM PM,但显示以实际为准
HH24:一天中的第几个小时(24小时制)
常用
SELECT TO_CHAR(SYSDATE,'HH24') FROM DUAL; -- 07
SELECT TO_CHAR(SYSDATE,'HH') FROM DUAL; -- 07
SELECT TO_CHAR(SYSDATE,'HH12 AM') FROM DUAL; -- 07 上午
SELECT TO_CHAR(SYSDATE,'HH12 PM') FROM DUAL; -- 07 上午
MI
:一小时中的第几分钟
SELECT TO_CHAR(SYSDATE,'MI') FROM DUAL; -- 07:57 57
SS
:一分钟中的第几秒
SELECT TO_CHAR(SYSDATE,'SS') FROM DUAL;
FF:默认六位
FF3 FF6:FF后面的数字表示毫秒显示位数
将
只含有数字
的字符串转化为数值去掉字符串中的特定符号(千分符和美元符)并将剩下的数字转化为数值
SELECT TO_NUMBER('12334') FROM DUAL; -- 12334 左靠齐
SELECT TO_NUMBER('SDD445SDFD') FROM DUAL; -- 出错 不是纯数字
SELECT TO_NUMBER('123,456,789','999999999.99') FROM DUAL; -- 123456789
SELECT TO_NUMBER('$123,456,789','$999999999.99') FROM DUAL; -- 123456789
TO_DATE(字符串表示的日期,’与前者对应的日期格式’)
一般需要和TO_CHAR() 连用
SELECT TO_DATE('20210312 09:27:30','YYYYMMDD HH24:MI:SS') FROM DUAL;
-- 字符2021/03/13 变成2021-03-13
SELECT TO_CHAR(TO_DATE('2021-03/13','YYYY/MM/DD'),'YYYY-MM-DD') FROM DUAL;
日期的加减:+1 表示加一天,换句话说日期的加减是按天计算的
两个日期型数据,不能做加法,只能做
减法得到的是天数
SELECT SYSDATE+1 FROM DAUL; -- 2021/03/14 8:22:52
SELECT TO_DATE('2021-12-31','YYYY-MM-DD') - TO_DATE('2021-12-30','YYYY-MM-DD') FROM DUAL; -- 得到天数
SELECT TO_DATE('2021-12-31','YYYY-MM-DD') + TO_DATE('2021-12-30','YYYY-MM-DD') FROM DUAL; --不能相加 错误
CONCAT(STR1,STR2):将括号内的两个字符串合并到一起.
ORCALE中的CONCAT只能将两个字符串进行合并
UPPER(STR):转大写 所有字母都大写
LOWER(STR):转小写 所有字母都小写
INITCAP(STR):将字符串STR中的每个单词改成首字母大
写其他字母手写(多个单词可以用空格及其他符号隔开 / $ ,
)
SELECT 'SMITH ALLEN' ENAME,INITCAP('SMITH ALLEN') FROM DUAL; -- Smith Allen
SELECT 'SMITH''ALLEN' ENAME,INITCAP('SMITH''ALLEN') FROM DUAL; -- 单引号成对出现
SELECT 'SMITH2ALLEN' ENAME,INITCAP('SMITH2ALLEN') FROM DUAL; -- Smith2allen 数字不作为分割符
REPLACE(STR,S1,S2):将STR中的S1替换成S2。
所有S1都要换成S2 当成整体
其中
S1和S2视为一个整体,不能单独看其中的某个字符
不写S2默认为空 ‘’
SELECT REPLACE('SSDDDFFFS','S','E') FROM DUAL; -- EEDDDFFFE
SELECT REPLACE('2020-01-12','-','/') FROM DUAL;
SELECT REPLACE('ABCDEF','A%F','WWW') FROM DUAL; -- 通配符 无效
TRIM(STR):去除两侧空格 默认去除函数 ,只能去除单个字符
LTRIM(STR [,S]):去除左侧空格,可以去除字符串 多个字符
RTRIM(STR [,S]):去除右侧空格,可以去除字符串 多个字符
TRIM([LEADING/TRAILING/BOTH] [S FROM] STR)
:
LEADING:去除头部S字符
TRAILING:去除尾部S字符
BOTH : 去除两侧字符
SELECT TRIM(LEADING 'A' FROM 'ABCDA') FROM DUAL; -- 去除左侧的A BCDA
SELECT TRIM(TRAILING 'A' FROM 'ABCDA') FROM DUAL; -- 去除右侧的A ABCD
SELECT TRIM(BOTH 'A' FROM 'ABCDA') FROM DUAL; -- 去除两侧的A BCD
注意:LTRIM() RTRIM()中的S若有多个字符,去除时挨个检索S中的每一个字符而不是视为一个整体
SUBSTR(STR,IND [, LEN]):从字符串STR的IND位置,截取LEN长度的字符串并返回该截取内容
针对字符 一个汉字 一个字母 一个符号都是一个字符
拓展: SUBSTRB() 针对字节 这里指存储长度 一个汉字对应的
字节数和字符集有关
注意
1 从0位开始截取,相当于从1位开始截取
2 如果IND参数为正,表示从左向右定位;如果IND参数为负,表示从右向左定位
3 无论IND参数是正还是负,截取都是从左向右截取
4 若LEN参数为0或者为负,截取结果返回空值
5 若LEN参数已超出STR实际长度,则截取到末尾
6 LEN参数可以省略,表示截取到末尾,而IND参数必不可少
7 无论IND 还是LEN 出现小数 都会被忽
SELECT ENAME,SUBSTR(ENAME,0,2) FROM EMP; -- 起始位置为0和起始位置为1的效果相同
SELECT ENAME,SUBSTR(ENAME,-2,2) FROM EMP; -- 起始位置为负数,定位时从右往左,截取仍是从左往右
SELECT ENAME,SUBSTR(ENAME,-2,0) FROM EMP; -- 截取长度为0 结果为空
SELECT ENAME,SUBSTR(ENAME,-2,-2) FROM EMP; -- 截取长度为负数 截取结果也是为空
SELECT ENAME,SUBSTR(ENAME,2,100) FROM EMP; -- 截取长度超过剩余长度,则有多少截多少
SELECT ENAME,SUBSTR(ENAME,2) FROM EMP; -- 不指定截取长度,则截到末位
SELECT ENAME,SUBSTR(ENAME,-200,2) FROM EMP; -- 截取长度超长,截取结果为空 √
SELECT ENAME,SUBSTR(ENAME,200,2) FROM EMP; -- 空
SELECT ENAME,SUBSTR(ENAME,-200) FROM EMP; -- 空
SELECT ENAME,SUBSTR(ENAME,1,1.5) FROM EMP; -- 无论起始位置 还是长度 出现小数 都会被忽略
LPAD(STR,N,S):在字符串STR左侧,填充S字符,使长度达到N
RPAD(STR,N,S):在字符串STR右侧,填充S字符,使长度达到N
不指定S 填充空格
S可以是某一个字符,也可以是一段字符串,若S是字符串,填充时会循环使用S中的每一个字符
尽量避免填充后的长度比STR小 出现这种之后会出现从左侧截取N个长度的字符
SELECT LPAD('ABCDEF',4,'=') FROM DUAL; -- ABCD
SELECT RPAD('ABCDEF',4,'=') FROM DUAL; -- ABCD
SELECT LPAD('ABCDEF',7,'') FROM DUAL; -- 填充物空 返回结果为空
SELECT RPAD('今天冻成',6,'狗') FROM DUAL; -- 填充长度按照字节来算
LPAD RPAD 针对字节 指的是显示长度,一个汉字对应的字节数固定为两个字节
LENGTH(STR):返回字符串STR的字符长度
针对的就是字符
LENGTHB(STR):返回字符串STR的字符
SELECT LENGTH('ABCDE') FROM DUAL;
SELECT * FROM EMP WHERE LENGTH(ENAME) = 5;
SELECT LENGTH('今天冻成狗'),LENGTHB('明天冻成狗') FROM DUAL; -- 5 10
INSTR(STR,S,IND,N):从第IND位开始,查找S在字符串中第N次出现的位置
针对字符
一般用法:INSTR(STR,S):查找字符串S在STR中第一次出现的位置
INSTRB()
针对字节
SELECT INSTR('ABCDEF','B') FROM DUAL ; -- 2
SELECT INSTR('ABCDEF','CD') FROM DUAL; -- 3 返回目标首位所在的位置
SELECT INSTR('ABCDEF','CF') FROM DUAL; -- 0 视为整体
SELECT INSTR('ABCDEABCDE','B',3) FROM DUAL; -- 7 无论起始位置从哪开始,返回所在位置都是从字符首位开始
注意
1)IND=0,返回0
SELECT INSTR('ABCDE','B',0) FROM DUAL; -- 0
2)IND<0,代表从右侧开始数起,第N次出现S的位置,返回位置时仍从左侧开始算起。
SELECT INSTR('ABCDE','B',-2) FROM DUAL; -- 2 定位:从右向左 查找:从右往左数 返回位置从左向右
3)IND超出字符总长时,返回0
SELECT INSTR('ABCDE','B',6) FROM DUAL; --0
4)N<=0,会报错
SELECT INSTR('ABCDE','B',1,0) FROM DUAL; -- error
SELECT INSTR('ABCDE','B',1,-2) FROM DUAL; -- error
5)N超过S在字符串中出现的次数,返回0
SELECT INSTR('AABBCCDD','B',2,3) FROM DUAL; -- 0
6)IND和N是小数,按整数部分运行
ABS(NUM):一般对计算结果进行取绝对值
SELECT ABS(1.23),ABS(-4.56) FROM DUAL;
CELL(NUM):向上取整,得到距离NUM最近的两个整数中较大的那个数
FLOOR(NUM):向下取整,得到距离NUM最近的两个整数中较小的那个数
无论正数还是负数 上:数轴指向的方向; 下:数轴背向的方向
SELECT CEIL(1.23) FROM DUAL; -- 2
SELECT FLOOR(1.23) FROM DUAL; -- 1
SELECT CEIL(-1.23) FROM DUAL; -- -1
SELECT CEIL(123),FLOOR(123) FROM DUAL; -- 123 123 -- 整数返回本身
MOD(X,Y):X除以Y得到的余数
分库分表方面应用取余函数
SELECT MOD(17,5) FROM DUAL;
ROUND(NUM [,P]):对数值NUM进行四舍五入,其中P表示精度
1 精度为0或者省略
SELECT ROUND(4.54321,0),ROUND(4.54321) FROM DUAL; -- 5 5 看小数点后面第一位 进行进位
2 负数的四舍五入
SELECT ROUND(-1.4),ROUND(-1.6) FROM DUAL; -- -1 -2 去掉负号 按照正数规则左四舍五入 再加上负号
3 精度为负数 -1 保留到十位 -2 保留到百位后面全部清0
SELECT SELECT ROUND(45.321,-1),ROUND(45.321,-2),ROUND(55.321,-2),ROUND(55.321,-3) FROM DUAL;
-- 50 0 100 0
4 精度不为整数:忽略小数
SELECT ROUND(5.4321,2.22222),ROUND(5.4321,2.99999) FROM DUAL;-- 5.43 5.43
5 精度过高:显示最简形式
SELECT ROUND(5.4321,10) FROM DUAL; -- 最简 5.4321
TRUNC:对数值NUM进行截断,其中参数P决定截断精度
1 精度为0或者省略
SELECT TRUNC(4.54321,0),TRUNC(4.54321) FROM DUAL; -- 4 4
2 负数的截取
SELECT TRUNC(-1.4),TRUNC(-1.6) FROM DUAL; -- -1 -1
3 精度为负数 -1 保留到十位 -2 保留到百位后面全部清0
SELECT TRUNC(45.321,-1),TRUNC(045.321,-2),TRUNC(055.321,-2),TRUNC(0055.321,-3) FROM DUAL;
-- 40 0 0 0
4 精度不为整数:忽略小数
SELECT TRUNC(5.4321,2.22222),TRUNC(5.4321,2.99999) FROM DUAL; -- 5.43 5.43
默认的精确到天 ,每天的12点为分界线
SELECT ROUND(SYSDATE,'DD'),ROUND(SYSDATE) FROM DUAL; -- 此时已经过完十二点 2021/03/14 2021/03/14
7月份为分界线 到七月份为下一年
ROUND(TO_DATE('20210630','YYYYMMDD'),'YYYY') FROM DUAL; --2021/01/01
SELECT ROUND(TO_DATE('20210730','YYYYMMDD'),'YYYY') FROM DUAL; --2022/01/01
每月的16号为分界线 与月份无关
SELECT ROUND(TO_DATE('20210215','YYYYMMDD'),'MM') FROM DUAL;--2021/02/01
SELECT ROUND(TO_DATE('20210216','YYYYMMDD'),'MM') FROM DUAL;--2021/03/01
中间月份的16号
显示下个季度的1号
例如 :第一季度 1-3 分界线为2月16
SELECT ROUND(TO_DATE('20210331','YYYYMMDD'),'Q') FROM DUAL; -- 2021/04/01 显示下个季度的1号
每个小时的30分为分界线
SELECT ROUND(TO_DATE('20210117 11:29:00','YYYYMMDD HH24:MI:SS'),'HH24') FROM DUAL; -- 11
SELECT ROUND(TO_DATE('20210117 11:30:00','YYYYMMDD HH24:MI:SS'),'HH24') FROM DUAL; -- 12
SELECT ROUND(TO_DATE('20210117 11:31:00','YYYYMMDD HH24:MI:SS'),'HH24') FROM DUAL; -- 12
MONTHS_BETWEEN(D1,D2):求D1和D2之间差几个月 D1 > D2
1)计算从元旦到今天经过了几个月(整数月)--加上取整函数
SELECT CEIL(MONTHS_BETWEEN(SYSDATE,TO_DATE('20210101','YYYYMMDD'))) FROM DUAL;
2)计算从元旦到今天经过了几个月(非整数月)
SELECT MONTHS_BETWEEN(SYSDATE,TO_DATE('20210101','YYYYMMDD')) FROM DUAL;
3)计算从今天到明年元旦还要等待几个月(整数月)
SELECT CEIL(MONTHS_BETWEEN(TO_DATE('20220101','YYYYMMDD'),SYSDATE)) FROM DUAL;
1)一般工作中使用到的情况是,给定两个月初或月末日期、或者两个DD相同的日期。如果给定的两个日期DD不相同,月份差会得到非整数,小数部分为剩余天数/31,这个规则无论针对哪一个月都是如此;
2)如果给定的两个日期DD不同,但是想要获得整数月,可以配合TRUNC函数或后续提到的获取月末日期函数使用;
3)如果两个日期的DD不同,但是都是月末日期,则获取的月份差将是整数;
4)需要特别注意,函数中录入的两个日期是有大小之分的(区别BETWEEN AND),一般晚的日期在前,早的日期在后,如此得到的结果便是正数,反之获得负数。如果不确定两个日期的大小,可搭配ABS()函数使用。
LAST_DAY(D):获取指定日期对应的当月最后一天,即月末日期
默认带上时分秒 使用TRUNC函数 可以去掉时分秒
SELECT TRUNC(LAST_DAY(SYSDATE)),LAST_DAY(SYSDATE) FROM DUAL; -- 2021/03/31 2021/03/31 13:31:17
-- 查询某月份的最大天数
SELECT LAST_DAY(SYSDATE) FROM DUAL;
SELECT TO_CHAR(LAST_DAY(SYSDATE),'DD') FROM DUAL; -- 31
ADD_MONTHS(D,N):在D日期的基础上加N个月 。 N 可正可负
SELECT SYSDATE,ADD_MONTHS(SYSDATE,2) FROM DUAL;
SELECT SYSDATE,ADD_MONTHS(TO_DATE('20210102','YYYYMMDD'),-2) FROM DUAL;
1 如果N是小数:小数部分没有意义
SELECT SYSDATE,ADD_MONTHS(SYSDATE,2.5) FROM DUAL; -- 2021/03/13 15:12:05 2021/05/13 15:12:05
2 如果D是月末:得到的是每个月的最后一天
SELECT SYSDATE,ADD_MONTHS(TO_DATE('20210131','YYYYMMDD'),-2) FROM DUAL; -- 2021/03/13 15:13:04 2020/11/30
NEXT_DAY(D,W):给定日期D和星期W,获取D日期之后的下一个星期W(不是下个周的周几) 返回一个日期
SELECT NEXT_DAY(SYSDATE,'星期日') FROM DUAL; -- 星期日作为每一周的第一天 2021/03/13 2021/03/14 15:16:00
SELECT TRUNC(TO_DATE('20210312','YYYYMMDD'),'IW')+6 FROM DUAL; -- 获取一周的最后一天 (星期日)
SELECT NEXT_DAY(TRUNC(TO_DATE('20210312','YYYYMMDD'),'IW')+6,'星期五'),TRUNC(TO_DATE('20210312','YYYYMMDD'),'IW')+6 FROM DUAL; -- 2021/03/19 2021/03/14
-- 先找到20210312这一周的周一,加上六找到这一周的周日,在找到下周五
DECODE(EXPR,VALUE1,RESULT1,VALUE2,RESULT2,…,DEF_RESULT) : 类似于CASE WHEN THEN
只能判断等于某个条件
使用decode函数,职位是分析员的,工资+1000;职位是经理的,工资+800;职位是其它的,工资+400
-- DECODE() 函数 不建议用
SELECT ENAME,JOB,SAL,DECODE(JOB,'ANALYST',SAL+1000,'MANAGER',SAL+800,SAL+400) FROM EMP;
COALESCE(c1,c2,c3,c4,…cn):返回括号中第一个非空表达式,如果都为空,则返回空
SELECT COALESCE(NULL,123,145) FROM DUAL; -- 123
SELECT COALESCE(NULL,NULL) FROM DUAL; -- 空
NVL(EXPR,VALUE):如果EXPR是空,返回一个VALUE,如果EXPR不为空,返回EXPR本身
NVL2(EXPR1,EXPR2,EXPR3):expr1不为NULL,返回expr2;expr1为NULL,返回expr3
SELECT NVL2(SAL+COMM,SAL,SAL+COMM)FROM EMP;
DISTINCT:紧跟SELECT后面
SELECT DISTINCT COL_LIST FROM TB_NAME … 对COL_LIST范围内的字段进行去重
获取各部门的各岗位
SELECT DISTINCT JOB,DEPTNO FROM EMP ;
SELECT DISTINCT DEPTNO,JOB FROM EMP ;
SELECT DEPTNO,JOB FROM EMP GROUP BY DEPTNO,JOB;