1、函数是用于执行数据处理和复杂运算的。通过对一组数据进行一系 列的运算,得到一个最终的需要输出的结果。函数通常都会有一个或多个输入,被称作函数的参数。
2、在函数的内部对输入的参数进行 逻辑计算,最终会得到一个或多个值,但是只有一个值作为函数的计算结果返回给客户函数可以出现在sql语句的各个位置,最常见的是出现在查询列表中
3、根据函数对多行输入数据的处理方式,函数分为单行函数和多行函数。
4、单行函数:每行作为一个输入参数,经过函数的计算得到每行的计算结果单行函数执行的操作影响表中每一行的显示效果
4、多行函数多多行输入值进行计算,得到多行对应的一个结果
1、单行函数对单行输入参数进行计算,在每行中取得的值作为函数的输入参数,在计算后将结果返回给用户。
2、每行都会得到相应的计算结果单行函数可以对数据进行复杂的计算,也可以进行数据类型的转换,还可以对特殊的数据进行处理。
3、另外,单行函数还可以嵌套。一个函数可以作为另一个函数的输入参数
单行函数可以分为:1、字符函数 2、数字函数 3、日期函数 4、其他函数 5、转换函数
--转换成小写的
SELECT E.ENAME, LOWER(E.ENAME) FROM EMP E WHERE LOWER(E.ENAME)='smith'
--转换成大写的
SELECT E.ENAME, UPPER(E.ENAME) FROM EMP E WHERE UPPER(E.ENAME)='SMITH'
--转换成驼峰的
SELECT E.ENAME, INITCAP(E.ENAME) FROM EMP E WHERE INITCAP(E.ENAME)='Smith'
--连接两个字符串,相当于||
SELECT ENAME || EMPNO || CONCAT(ENAME,EMPNO) FROM EMP E WHERE SAL > 1000;
--截取字符串SUBSTR(c1,n1[,n2])
--在字符表达式c1里,从n1开始取n2个字符;若不指定n2,则从第y个字符直到结束的字串.
SELECT ENAME,SUBSTR(ENAME,1,2) FROM EMP
--INSTR在一个字符串中搜索指定的字符,返回发现指定的字符的位置,没有返回0;
SELECT ENAME,INSTR(ENAME,'A') FROM EMP
--LPAD(c1,n[,c2]) 在字符串c1的左边用字符串c2填充,直到长度为n时为止
SELECT EMPNO,LPAD(ENAME,10,'**'),RPAD(JOB,10,'*') FROM EMP;
--REPLACE(c1,c2[,c3]) 将字符表达式值中,部分相同字符串,替换成新的字符串
SELECT REPLACE(ENAME,'A','a') FROM EMP;
-- 截取,使用数字函数
SELECT TRUNC(49.536 ,1) AS "小数点后一位" ,
TRUNC(49.536 ,0)AS "个位",
TRUNC(49.536 ,-1) "十位"
FROM SYS.DUAL
--取余函数
SELECT E.ENAME, E.SAL, MOD(E.SAL,300) AS "除以300后的余数" FROM EMP E WHERE E.SAL IS NOT NULL;
--四舍五入,用到其他用户的表,需要使用方案名.表名
SELECT ROUND(45.945,2) "小数点后两位",
ROUND(45.945,0) "个位",
ROUND(45.945,-1) "十位"
FROM SYS.DUAL ;
1、日期函数对日期数据进行处理,得到新的日期或者数字
2、Oracle以内部数字格式存储日期:世纪,年,月,日,小时,分钟,秒 缺省的日期格式是 DD-MON-YY
3、Oracle内置的SYSDATE函函数,是返回日期和时间的函数
-- 使用日期函数
SELECT SYSDATE AS "时间" FROM SYS.DUAL;
-- 日期-日期得到两个日期的差(是一个数值类型)
SELECT E.ENAME , ROUND((SYSDATE-E.HIREDATE)/365,0) AS "工作年龄"FROM EMP E;
-- 日期+数值得到一个新的日期
SELECT E.ENAME,E.HIREDATE 雇用日期,(E.HIREDATE + 90) AS "转正日期" FROM EMP E
--两个日期相差多少个月
SELECT E.ENAME ,MONTHS_BETWEEN(SYSDATE,E.HIREDATE) AS "工作的月数" FROM EMP E;
-- 添加几个月
SELECT E.ENAME,E.HIREDATE 雇用日期,ADD_MONTHS(E.HIREDATE,3) AS "转正日期" FROM EMP E
--下周星期几
select sysdate 当时日期,
next_day(sysdate,'星期一') 下周星期一,
next_day(sysdate,'星期二') 下周星期二,
next_day(sysdate,'星期三') 下周星期三 from dual;
-- 当前月的最后一天
SELECT E.HIREDATE, LAST_DAY(E.HIREDATE) FROM EMP E;
--将字符串转为日期格式
SELECT E.ENAME ,E.HIREDATE FROM EMP E WHERE E.HIREDATE > TO_DATE('23-9-1982','DD-MM-YYYY')
--最近时期
select sysdate 当时日期,
round(sysdate) 最近0点日期,
round(sysdate,'day') 最近星期日,
round(sysdate,'month') 最近月初,
round(sysdate,'q') 最近季初日期,
round(sysdate,'year') 最近年初日期 from dual;
-- 转换函数
-- 隐式转换
SELECT * FROM EMP WHERE DEPTNO = 20
SELECT * FROM EMP WHERE DEPTNO = '20'
-- 日期类型转化为文本类型
SELECT E.ENAME ,e.hiredate, TO_CHAR(E.HIREDATE,'YYYY-MM-DD:DAY') AS "日期" FROM EMP E
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS AM DAY') 时间 FROM SYS.DUAL;
在转换日期为字符串的时候,日期中的年,月,日,时。分, 秒都需要相应的格式控制符来控制转换的显示格式
格式 | 描述 |
---|---|
YYYY,YYY,YY | 分别代表4位,3位,2位的数字年份 |
YEAR | 年份的全拼 |
MM | 数字月 |
MONTH | 月份的全拼名称 |
MON | 月份的缩写 |
DD | 数字日 |
DAY | 星期的全拼 |
DY | 星期的缩写 |
与日期控制符相对应,时间也需要相应的格式控制符来描述。 否则默认是不显示时间的。即使包含了时间,日期格式控制 符也比较简单
格式控制字符 | 描述 |
---|---|
AM | 表示上午或下午 |
HH24,HH12 | 24小时制或12小时制 |
MI | 分钟 |
SS | 秒钟 |
SP | 数字的拼写 |
TH | 数字的序数词 |
特殊字符 | 在日期中加入特殊的字符 |
--使用TO_CHAR把数值转为字符
SELECT ENAME,TO_CHAR(SAL,'$9999,99.00'),TO_CHAR(SAL,'L000,000.00') FROM EMP;
格式控制字符 | 描述 |
---|---|
9 | 代表一个数字(有数字,就显示,没有就不显示) |
0 | 强制显示0 |
$ | 放值一个$符 |
L | 放值一个本地货币符 |
, | 显示千位指示符 |
. | 显示小数点 |
··
-- TO_NUMBRE ,把字符型的数据转为数值,字符的格式和模板的模式必须要一致
SELECT TO_NUMBER('$800.00','$999,999.00') FROM SYS.DUAL
-- TO_DATE,把字符型的数据转为日期型的数据
SELECT * FROM EMP WHERE HIREDATE = TO_DATE('1981-2-20','YYYY-MM-DD');
--其他函数
-- 在算术表达式中出现null,得到的结果就是NULL
SELECT EMPNO,ENAME,SAL,COMM ,(SAL*12+COMM) "年收入1" ,(SAL*12 + NVL(COMM,0)) "年收入2"FROM EMP;
--如果上级为NULL,就是老板
SELECT ENAME,JOB,NVL(TO_CHAR(MGR),'BOSS') FROM EMP
--如果expr不为Null,返回expr1, 为Null,返回expr2.
SELECT ENAME,JOB, NVL2(JOB,JOB,'没工作') FROM EMP;
--NULLIF(expr1,expr2) 比较两个表达式,如果相等返回空值,如果不等返回第一个表达式
SELECT ENAME,JOB , NULLIF( LENGTH(ENAME),LENGTH(ENAME) ) ,NULLIF( LENGTH(ENAME),LENGTH(JOB) ) FROM EMP
-- CASE实现IF..ELSE IF ...ELSE的功能
SELECT ENAME,
JOB,
SAL,
CASE JOB
WHEN 'CLERK' THEN
1.10 * SAL
WHEN 'MANAGER' THEN
1.3 * SAL
WHEN 'SALESMAN' THEN
1.45 * SAL
ELSE
SAL
END AS "修订工资数"
FROM EMP
WHERE ENAME = 'SMITH';
SELECT ENAME,
JOB,
SAL,
DECODE(JOB,
'CLERK',
SAL * 1.10,
'MANAGER',
SAL * 1.30,
'SALESMAN',
SAL * 1.4,
SAL) AS "修订工资数"
FROM EMP
WHERE ENAME = 'SMITH';
-- 组函数,就是我们前面提到的多行函数
SELECT MAX(SAL)FROM EMP;
-- AVG,SUN只能针对数值型的数据
-- MAX,MIN,COUNT可以针对任何类型的数据
SELECT MAX(SAL),MIN(SAL),AVG(SAL),SUM(SAL) FROM EMP;
SELECT MAX(ENAME),MIN(ENAME) FROM EMP;
SELECT MAX(HIREDATE) ,MIN(HIREDATE) FROM EMP
-- COUNT有两种用法
--1.count(*) 查询数据的总条数
--2.COUNT(字段) ,这种情况下,忽略空值
SELECT COUNT(*) FROM EMP;
SELECT COUNT(COMM) FROM EMP;
-- 所有的组函数都是忽略空值的
SELECT SUM(COMM) ,AVG(COMM) ,COUNT(COMM) ,SUM(COMM)/COUNT(COMM) FROM EMP;
--按照人数计算平均奖金
SELECT SUM(COMM)/COUNT(*) ,AVG( NVL(COMM,0))FROM EMP
--注意区分
SELECT SUM(COMM)/COUNT(*) ,AVG(COMM)FROM EMP
-- 对数据进行分组后,使用组函数
-- 1.出现在查询列表中字段,要么出现在组函数中,要么出现在GROUP BY子句中
-- 2.也可以只出现在GROUP BY中
SELECT MAX(SAL) FROM EMP GROUP BY DEPTNO;
SELECT DEPTNO, MAX(SAL) FROM EMP GROUP BY DEPTNO;
-- JOB不合法
SELECT DEPTNO, JOB,MAX(SAL) FROM EMP GROUP BY DEPTNO;
-- 按照多个字段进行分组(合法)
SELECT DEPTNO,JOB ,MAX(SAL) FROM EMP GROUP BY DEPTNO,JOB ORDER BY DEPTNO;
--DEPTNO不合法
SELECT DEPTNO,MAX(SAL) FROM EMP;
-- 要对分组以后的数据进行过滤,过滤>=3000的记录,不能使用WHERE子句,而是要使用HAVING子句
SELECT MAX(SAL)
FROM EMP
WHERE MAX(SAL) >= 3000
GROUP BY DEPTNO;
SELECT DEPTNO, MAX(SAL)
FROM EMP
GROUP BY DEPTNO
HAVING MAX(SAL) >= 3000
ORDER BY DEPTNO;
-- 首先用WHERE对数据过滤,过滤后的数据用GROUP BY 分组,分组后的数据用HAVING再过滤,过滤后的数据用ORDER BY 排序
SELECT DEPTNO,MAX(SAL)
FROM EMP
WHERE DEPTNO IS NOT NULL
GROUP BY DEPTNO
HAVING MAX(SAL) >= 3000
ORDER BY DEPTNO;
-- 组函数也可以嵌套,
-- 在组函数嵌套的时候,必须要使用GROUP BY
-- 组函数最多能嵌套两层
SELECT DEPTNO, MAX(SAL)
FROM EMP
WHERE DEPTNO IS NOT NULL
GROUP BY DEPTNO
SELECT MAX( MAX(SAL))
FROM EMP
WHERE DEPTNO IS NOT NULL
GROUP BY DEPTNO
--不合法
SELECT MAX(MAX(SAL))
FROM EMP
WHERE DEPTNO IS NOT NULL
-- 多表查询:用单个的Select语句从多个表中查询相关的数据
-- 在多表查询的时候,如果没有加入等值条件,会产生笛卡尔乘积
SELECT EMP.EMPNO,EMP.ENAME,EMP.DEPTNO,DEPT.DEPTNO,DEPT.DNAME,DEPT.LOC
FROM EMP,DEPT
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC
FROM EMP E,DEPT D
WHERE E.DEPTNO = D.DEPTNO;
-- 多表查询分为不同的标准
-- ==============================SQL1992老标准=================================
--1.等值查询,在父子表的关系上,用 = 来两家两个表的两个字段或多个表的多个字段
-- 等值查询只能查询两个表中一一对应的数据,该部门有员工,该员工有所属部门
-- N个表的等值查询,需要N-1个等值条件
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E,DEPT D
WHERE E.DEPTNO = D.DEPTNO;
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC
FROM EMP E,DEPT D
WHERE E.DEPTNO = D.DEPTNO AND E.EMPNO=7369;
SELECT * FROM BONUS;
SELECT E.*,D.*,L.*
FROM EMP E,DEPT D,LOCATIONS L
WHERE E.DEPTNO = D.DEPTNO AND D.LOC_ID = L.LOCID AND E.EMPNO = 7369;
-- 非等值查询,两个表之间没有父子关系,用!=来链接两个表
SELECT E.EMPNO,E.ENAME,E.SAL,S.GRADE,S.LOSAL,S.HISAL
FROM EMP E,SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL AND EMPNO = 7369;
-- 自连接(子链接),通过别名,将一个表虚拟成两个表,然后在这两个表上做等值查询
SELECT E.EMPNO,E.ENAME ,M.EMPNO,M.ENAME
FROM EMP E,EMP M
WHERE E.MGR = M.EMPNO AND E.EMPNO = 7369;
-- 外链接:在等值查询的基础之上,可以查询不满足等值条件的数据
-- 左外链接,可以把右边表中不满足等值条件的数据查询出来
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME
FROM EMP E,DEPT D
WHERE E.DEPTNO(+) = D.DEPTNO;
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME
FROM EMP E,DEPT D
WHERE D.DEPTNO (+) = E.DEPTNO;
-- 右外链接,可以把左边表中不满足等值条件的数据查询出来
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E,DEPT D
WHERE E.DEPTNO = D.DEPTNO (+);
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E,DEPT D
WHERE D.DEPTNO = E.DEPTNO (+);
-- + 不能同时出现在= 的两边
-- ==================================SQL1999新标准=====================================
--1.交叉链接,就相当与SQL1992老标准等值查询的时候没有给出正确的等值条件,会产生笛卡尔乘积
SELECT E.*,D.*
FROM EMP E
CROSS JOIN DEPT D;
--2.自然链接:在父子表关系上,自动的匹配两个表中列名完整相同的字段(参照列),在这些相同名称的字段上做等值查询
-- 参照列上面不能使用前缀
-- 自然链接的缺陷:1.会把所有的参照列都作为等值条件;2.如果参照列的类型不同,查询会报错
SELECT E.EMPNO,E.ENAME,DEPTNO,D.DNAME
FROM EMP E
NATURAL JOIN DEPT D;
-- 当两个表中没有参照列的时候,自然查询会产生笛卡尔乘积
SELECT D.DEPTNO,D.DNAME,L.LOCNAME
FROM DEPT D
NATURAL JOIN LOCATIONS L;
-- 3.JOIN ..USING ,在自然链接的基础上,加以改进,使用指定的参照列来作为等值条件
SELECT E.EMPNO,E.ENAME,DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E
JOIN DEPT D USING(DEPTNO)
WHERE E.EMPNO = 7369;
-- 4.JOIN.. ON,使用on里面指定的条件作为查询条件(ON里面的条件可以是任意的条件)
SELECT E.*,D.*
FROM EMP E
JOIN DEPT D ON (E.DEPTNO= D.DEPTNO);
SELECT D.*,L.*
FROM DEPT D
JOIN LOCATIONS L ON(D.LOC_ID = L.LOCID);
-- 使用JOIN.. ON做N个表的等值查询,需要N-1个JOIN.. ON子句
SELECT E.*,D.*,L.*
FROM EMP E
JOIN DEPT D ON (E.DEPTNO=D.DEPTNO)
JOIN LOCATIONS L ON (D.LOC_ID = L.LOCID)
WHERE E.EMPNO = 7369;
SELECT E.*,D.*,L.*
FROM EMP E
INNER JOIN DEPT D ON (E.DEPTNO=D.DEPTNO)
INNER JOIN LOCATIONS L ON (D.LOC_ID = L.LOCID)
WHERE E.EMPNO = 7369;
-- 使用JOIN..ON做非等值查询
SELECT E.EMPNO,E.ENAME,S.GRADE,S.LOSAL,S.HISAL
FROM EMP E
JOIN SALGRADE S ON (E.SAL BETWEEN S.LOSAL AND S.HISAL)
WHERE E.EMPNO=7369;
-- OUTER JOIN ...ON 外链接
-- LEFT OUTER JOIN ..ON() 可以把左边表中不满足等值条件的数据查询出来
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E
LEFT OUTER JOIN DEPT D ON (E.DEPTNO= D.DEPTNO);
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM DEPT D
LEFT OUTER JOIN EMP E ON ( D.DEPTNO = E.DEPTNO)
-- RIGHT OUTER JOIN ..ON()可以把右边表中不满足等值条件的数据查询出来
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E
RIGHT OUTER JOIN DEPT D ON (E.DEPTNO= D.DEPTNO);
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM DEPT D
RIGHT OUTER JOIN EMP E ON (E.DEPTNO= D.DEPTNO);
-- FULL OUTER JOIN ..ON(),可以把左右两边表中不满足等值条件的数据都查询出来
SELECT E.EMPNO,E.ENAME,E.DEPTNO,D.DEPTNO,D.DNAME,D.LOC_ID
FROM EMP E
FULL OUTER JOIN DEPT D ON (E.DEPTNO= D.DEPTNO);
-- 使用UNION把两个结果集合并成一个结果集,注意两个查询语句的结果集必须要一致才能合并
-- 结果集必须要一致指的是查询字段的个数,字段的类型,字段的顺序 必须要一致
-- 使用查询语句来创建表(复制表及其数据,但是并没有复制主外键)
CREATE TABLE DEPT_BAK
AS
SELECT * FROM DEPT;
-- UNION ,会去除重复的数据
SELECT * FROM DEPT_BAK
UNION
SELECT * FROM DEPT
-- UNION ALL ,不去除重复的数据
SELECT * FROM DEPT_BAK
UNION ALL
SELECT * FROM DEPT
--====================================子查询=============================================
-- 查询 比 7566员工的工资高的人的信息
SELECT SAL
FROM EMP
WHERE EMPNO = 7566
SELECT *
FROM EMP
WHERE SAL > 2975;
-- 为了给主查询提供条件,而首先执行的一个查询,被成为子查询(子查询是用来给主查询提供查询条件的)
-- 子查询首先被执行
-- 主查询使用子查询的查询条件
-- 子查询通常出现在比较运算符的右边,并且用()包围起来,便于理解
SELECT *
FROM EMP
WHERE SAL > (SELECT SAL FROM EMP WHERE EMPNO = 7566);
-- 子查询的分类,就是根据子查询的返回结果来区分的
-- 单行单列子查询,要使用单行比较运算符
-- 单行单列子查询
SELECT *
FROM EMP
WHERE SAL < (SELECT SAL FROM EMP WHERE EMPNO = 7566);
-- z子查询中使用了组函数,但是子查询的结果依旧是单行单列,仍然可以使用单行比较操作符
SELECT *
FROM EMP
WHERE SAL < (SELECT AVG(SAL) FROM EMP);
-- 子查询返回的结果是多行单列,就不能使用单行比较操作符
SELECT *
FROM EMP
WHERE SAL < (SELECT AVG(SAL) FROM EMP GROUP BY DEPTNO );
-- 子查询出现在HAVING中
-- HAVING对分组后的数据进行过滤
SELECT DEPTNO,MAX(SAL)
FROM EMP
GROUP BY DEPTNO
HAVING MAX(SAL) > (SELECT MAX(SAL) FROM EMP WHERE DEPTNO=20)
-- 子查询没有结果,主查询也不会报错,就是没有查询结果而已
SELECT *
FROM EMP
WHERE SAL > (SELECT SAL FROM EMP WHERE EMPNO=8000);
-- 多行单列子查询,要使用多行比较运算符,IN,ANY,ALL
SELECT JOB FROM EMP WHERE SAL >3000
-- 使用in运算符
SELECT E.EMPNO,E.ENAME,E.JOB,E.SAL
FROM EMP E
WHERE E.JOB IN (SELECT JOB FROM EMP WHERE SAL >3000);
-- 使用ALL运算符
-- > ALL ,大于子查询的最大值
SELECT E.*
FROM EMP E
WHERE E.SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO=30);
-- < ALL,小于子查询的最小值
SELECT E.*
FROM EMP E
WHERE E.SAL < ALL (SELECT SAL FROM EMP WHERE DEPTNO=30);
-- ANY
-- > ANY 大于子查询的最小值
SELECT E.*
FROM EMP E
WHERE E.SAL > ANY (SELECT SAL FROM EMP WHERE DEPTNO=10);
-- < ANY,小于子查询的最大值
SELECT E.*
FROM EMP E
WHERE E.SAL < ANY (SELECT SAL FROM EMP WHERE DEPTNO=20);
-- 多行多列子查询,可以使用in比较运算符
SELECT MGR,JOB FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369
-- 成对的比较
-- 查询和7566,7369同经理同职位的员工的信息
SELECT EMPNO,ENAME,MGR,JOB
FROM EMP
WHERE (MGR,JOB) IN (SELECT MGR,JOB FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369)
AND EMPNO !=7566 AND EMPNO != 7369;
-- 非成对的比较,把多行多列的子查询拆分成两个多行单列的子查询,分别使用IN运算符
SELECT EMPNO,ENAME,MGR,JOB
FROM EMP
WHERE MGR IN (SELECT MGR FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369)
AND JOB IN (SELECT JOB FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369)
AND EMPNO !=7566 AND EMPNO != 7369;
SELECT MGR FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369
SELECT JOB FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369
SELECT MGR,JOB FROM EMP WHERE EMPNO = 7566 OR EMPNO =7369
-- 子查询是出现在FROM后面的,用来提供数据源的,子查询被虚拟成一个表
SELECT 部门编号,MAXSAL,AVGSAL
FROM (SELECT DEPTNO 部门编号,
MAX(SAL) MAXSAL,
MIN(SAL) MINSAL,
AVG(SAL) AVGSAL,
SUM(SAL) SUMSAL
FROM EMP
WHERE DEPTNO IS NOT NULL
GROUP BY DEPTNO)
WHERE 部门编号 = 20