单表查询
空值转换为实际值
相对于nvl来说,coalesce支持多个参数,能很方便的返回第一个不为空的值。
1 | SELECT coalesce(comm,0) FROM emp; |
随机返回n条记录
正确写法:先随机排序,再取数据
1 | SELECT empno,ename |
错误写法:先取数据,再随机排序
1 | SELECT empno,ename FROM emp WHERE rownum <=3 ORDER BY dbms_random.value; |
模糊查询(转义)
在LIKE子句中有两个通配符:“%”(替代一个或多个字符)、“_”(替代一个或多个字符)。“_”被当作通配符了怎么办,可以用转义字符:
1 | SELECT * FROM v WHERE vname LIKE '\_BCE%' ESCAPE '\'; |
1 | SELECT * FROM v WHERE vname LIKE '_\BCE%' ESCAPE '\'; |
查询结果排序
以数字指定排序列
1 | # 按第三列排序 |
translate
语法格式:TRANSLATE(expr, from_string, to_string)
from_string 与 to_string 以字符为单位,对应字符一一替换。
1 | SELECT TRANSLATE('ab 你好 bcadefg', 'abcdefg', '1234567') AS NEW_STR FROM DUAL; |
如果 to_string 为空,则返回空值。
1 | SELECT TRANSLATE('ab 你好 bcadefg', 'abcdefg', '') AS NEW_STR FROM DUAL; |
如果 to_string 对应的位置没有字符,删除 from_string 中列出的字符将会被消掉。
1 | SELECT TRANSLATE('ab 你好 bcadefg', '1abcdefg', '1') AS NEW_STR FROM DUAL; |
处理排序空值
Oracle 默认排序空值在后面,如果想把空值(如 emp.comm)显示在前面:
1 | SELECT ename,sal,comm,nvl(comm, -1) order_col FROM emp ORDER BY 4; |
其实可以用关键字 NULLS FIRST 和 NULLS LAST:
1 | # 空值在前 |
操作多个表
rowid
除了用唯一列、主键列外,还可以使用 rowid
1 | SELECT ROWID,deptno FROM emp WHERE mgr = 7698; |
如果数据不是取自表,而是取自VIEW
1 | select rowid from v; |
查询期间创建临时视图
仅在查询期间存在,查询结束消失
1 | WITH e AS (SELECT ROWNUM AS sn,deptno,mgr,job FROM v) |
explain分析查询语句
1 | EXPLAIN PLAN FOR SELECT empno,ename FROM emp; |
join查询不同写法
INNER JOIN
1 | SELECT 1.str AS left_str, r.str AS right_str |
LEFT JOIN
1 | SELECT l.str AS left_str, r.str AS right_str |
RIGHT JOIN
1 | SELECT l.str AS left_str, r.str AS right_str |
FULL JOIN 无(+)的写法。
not in (空值) 返回空
在Oracle中子查询()中包含空值,NOT IN (空值) 返回为空。
1 | SELECT COUNT(*) FROM dept WHERE deptno NOT IN (SELECT emp.deptno FROM emp);# 返回0行 |
插入、更新与删除
使用默认值default
1 | CREATE TABLE TEST ( |
组织对某几列插入
c4列默认值为 SYSDATE,如何控制不允许手动录入:
1 | # 建一个不包括 c4 列的VIEW,通过VIEW新增数据 |
注意:通过VIEW新增数据,不能再使用关键字DEFAULT。
1 | INSERT INTO v_test(c1,c2,c3) VALUES (default,NULL,'不能改c4'); |
复制表的定义与数据
1 | # 方法一 |
限制数据录入
约束条件比较简单时,可以直接加在表中,如工资必须大于0:
1 | alter table emp add constraints ch_sal check(sal>0); |
但有些复杂或特殊的约束条件是不能放在表里的,如雇佣日期大于当前日期:
1 | alter table emp add constraints ch_hiredate check(hiredate>=sysdate); |
我们可以使用加了 WITH CHECK OPTION 关键字的 VIEW 来达到目的。如下我们限制了不符合内联视图条件的数据(SYSDATE+1):
1 | INSERT INTO |
多表插入语句
建两个测试用表:
1 | CREATE TABLE emp1 AS SELECT empno,ename,job FROM emp WHERE 1=2; |
无条件 INSERT:
1 | INSERT ALL |
有条件 INSERT ALL:
1 | delete emp1; |
INSERT FIRST 就不一样:
1 | delete emp1; |
转置 INSERT 与其说是一个分类,不如算 INSERT ALL 的一个用法:
1 | DROP TABLE T1; |
用其他表中值更新
1 | # 方法一 |
合并记录
1 | MERGE INTO bonuses d |
- 更新、删除、插入这三个操作同时进行,不分先后。
- 不能更新 JOIN 列。
- UPDATE的范围是:匹配到的数据减去删除的数据。(d.employee_id = s.employee_id)减去范围(s.salary > 8000)。
删除名称重复数据
1 | # 方法一:通过name相同、id不同的方式来判断 |
使用字符串
遍历字符串
1 | SELECT LEVEL FROM DUAL CONNECT BY LEVEL<=4; |
1 | WITH V AS (SELECT '天天向上' AS 汉字,'TTXS' AS 首拼 FROM DUAL) |
1 | WITH V AS (SELECT '天天向上' AS 汉字,'TTXS' AS 首拼 FROM DUAL) |
字符串中包含引号
1 | # 方法一:一个单引号换成两个单引号表示 |
计算字符串出现次数
若分割符只有一个:
1 | # 方法一:regexp_count |
若分割符有多个:
1 | # 错误写法: |
(regexp_)replace字符串替换
1 | # 除了用translate |
字符和数字数据分离
1 | select data from dept2; |
wm_concat替代品
wm_concat是一个非公开函数,具有不稳定性(返回值原来是varchar,Oracle11.2下成了clob),Oracle11.2开始有了分析函数listagg
1 | # 同sum一样,listagg起汇总作用,把字符串连在一起 |
regexp_substr分解IP
1 | # regexp_substr(参数1,参数2,参数3,参数4) |
按字母重排序字符串
ADAMS->AADMS
- 把字符串拆分为单个字母显示。
- 把多行数据合并为一行显示。
1 | # 1.拆分 |
rpad()字符串填充函数
rpad(string,padded_length,[pad_string]),从右边对字符串使用指定的字符进行填充:
- string 表示:被填充的字符串
- padded_length 表示:字符的长度,是返回的字符串的数量,如果这个数量比原字符串的长度要短,rpad函数将会把字符串截取成从左到右的n个字符;
- pad_string 是个可选参数,这个字符串是要粘贴到string的右边,如果这个参数未写,lpad函数将会在string的右边粘贴空格。
1 | # 例: |
使用数字
生成累计和
1 | select empno as 编号, |
分析函数 “sum(sal) over(order by empno)” 的结果(4100)是排序 “over(order by empno)” 后第一行到当前行的所有工资(1600+1250+1250)之和。
计算累计差
- 对数据排序
- 待减数变为负数
- 用累加计算累计差
1 | select * from detail; |
三种方式生成序号排名
1 | select deptno,empno,sal, |
生成序号排名取值
- 计算不同工资出现次数。
- 按次数排序生成序号。
- 根据序号过滤取值。
1 | select sal from ( |
利用 partition by 子句分别查询各部门哪个工资等级的员工最多。
1 | select deptno,sal from ( |
返回最值所在行数据
标量:先取出最大值,然后与最大值关联
1 | select deptno,empno,ename,sal, |
分析函数:可以直接满足上述需求,并同时取最大及最小值
1 | select deptno,empno,ename,sal, |
first_value
1 | select deptno,empno,ename,sal, |
求占总和的比例
部门 | 工资合计 | 总合计 | 工资比例 |
---|---|---|---|
10 | 8750 | 29025 | 30.15 |
20 | 10875 | 29025 | 37.47 |
30 | 9400 | 29025 | 32.39 |
- 分组汇总
- 通过分析函数获取总合计
- 得到总合计后计算比例
1 | # over()后不加任何内容时,表示对所有数据汇总 |
用专用比例函数 “ratio_to_report” 直接计算:
1 | select deptno,round(ratio_to_report(工资合计) over() * 100, 2) as 工资比例 |
同其他分析函数可以使用 partition by 分组计算,查询各员工占本部门的工资比例:
1 | select deptno,empno,ename,sal, |
日期运算
加减日、月、年
1 | select hiredate as 聘用日期, |
加减时、分、秒
1 | select hiredate as 聘用日期, |
日期间隔(时、分、秒)
两个date相减,得到的就是天数:
1 | SELECT |
日期间隔(日、月、年)
1 | SELECT |
取上/下一条记录日期
1 | # lag上一条,lead下一条 |
日期操作
常用取值
一些例子:
1 | SELECT |
注:last_day函数返回的时分秒与参数时间中的一样,用作区间条件时需注意。
1 | # 错误(未选定行): |
EXTRACT
与 TO_CHAR 一样,EXTRACT 可以提取时间字段中的年月日时分秒,但不能提取 DATE 中的时分秒,返回值为 NUMBER 类型。
1 | SELECT |
判断是否闰年
只要判断二月的月末是哪一天
1 | SELECT trunc(sysdate,'y') AS 年初 FROM DUAL; |
周的计算
1 | WITH X AS ( |
年内所有周内某天
返回指定年份内的所有周五:
1 | WITH X AS ( |
月内首/末个周内某天
返回当月内第一个周一与最后一个周一,分别找上月末及当月末之前七天的下一周周一即可:
1 | SELECT |
创建本月日历
- 枚举指定月份所有的日期
- 转换为对应的周信息
- 按所在周做一次“行转列”
1 | WITH |
全年日历
注意:第一周的数据 to_char(日期,’iw’) 返回值为去年第53周,需转换。
1 | WITH |
季度的开始结束日期
使用场景:生成汇总报表时,按季度分类汇总。
1 | SELECT sn AS 季度, |
补充范围内缺少的值
使用场景:业务数据不连续,为了统计显示美观,补充并给予默认值。
1 | WITH x AS |
使用日期特殊部分比较
使用场景:统计相同月份与周内日期聘用的员工。
1 | # 使用分析函数 count() over() 只访问一次emp,效率高 |
识别重叠的日期范围
使用场景:很多员工在旧工程结束之前就开始了新的工程,现要求返回这些工程时间重复的数据。
1 | # 使用分析函数 LAG 和 LEAD,分别访问结果集的前一行和后一行 |
按指定间隔汇总数据
使用场景:按指定时间间隔(10分钟)汇总数据。
- 截取到分钟,提取分钟信息。
- 对10取余。
- 时间减去余数。
1 | # MOD为取余函数 |