说明
本文主要介绍SQL语句常用的几种高级查询和子句
会介绍注意事项和结论
高级语句
- 分组查询
- 常用函数
- group by 子句
- group by rollup子句
- having 子句
- order by子句
- 多表查询
- 子查询
- 综合查询
- 常见问题或其他内容可以参考ORACLE学习之ORACLE常见错误分析和例子
分组查询
定义:返回一组数据经过分组函数处理后的最终值,且是一个值
分组查询中的常用函数
- AVG
- SUM
- MAX
- MIN
- COUNT
- WM_CONCAT
AVG,SUM,MAX,MIN,COUNT分组函数代码举例
SELECT SUM(s.sal) AS sum_sal,
AVG(s.sal) AS avg_sal,
MAX(s.sal) AS max_sal,
MIN(s.sal) AS min_sal,
COUNT(*) AS count_sal
FROM hyz_emp s;
--分组函数会忽略NULL值
显示结果
WM_CONCAT分组函数代码举例
SELECT s.comm
FROM hyz_emp s
WHERE s.deptno = 30
AND s.empno IN (6299, 2932, 7490);
--此时查询结果为如下所示的列
/*
1.00
NULL
1.00
*/
SELECT wm_concat(s.comm) AS wm_concat_sal
FROM hyz_emp s
WHERE s.deptno = 30
AND s.empno IN (6299, 2932, 7490);
--列转行应显示为1,1
--转换后为CLOB类型数据, 且忽略NULL值
显示结果
注意事项:
- 分组函数皆忽略NULL值,一定要注意
- 空字符串和NULL不是一个概念,空字符串是占位且实际存在的数据,而NULL无意义
GROUP BY和GROUP BY ROLLUP子句代码举例
SELECT s.deptno, AVG(s.sal) as sal FROM hyz_emp s GROUP BY s.deptno
--SELECT s.deptno, AVG(s.sal),s.empno as sal FROM hyz_emp s GROUP BY s.deptno;--(一)
--SELECT s.deptno, AVG(s.sal),s.empno as sal FROM hyz_emp s;--(二)
SELECT s.deptno, s.job, s.mgr, SUM(s.sal) AS sal
FROM hyz_emp s
GROUP BY ROLLUP(s.deptno, s.job, s.mgr)
HAVING s.job IS NOT NULL AND s.mgr IS NOT NULL
ORDER BY s.deptno;
显示结果
注意事项
- group by后的字段根据实际情况需要而选择,如果已定,select子句中可包含此字段也可不包含此字段,但是不能出现group by后未出现非分组函数的字段,例如代码报错部分(一)
- 反之,select子句中出现分组函数的字段,那么非分组函数的字段必须使用group by,例如代码报错部分(二)
- 同理如果使用group by语句,那么ORDER BY 只能使用分组函数group by中出现的字段
HAVING子句和ORDER BY子句代码举例
SELECT s.deptno, s.job, AVG(s.sal) AS sal
FROM hyz_emp s
HAVING AVG(s.sal) BETWEEN 1000 AND 2000
GROUP BY s.deptno, s.job
ORDER BY s.deptno, s.job;
--等同如下语句,但是排序不同
SELECT s.deptno, s.job, AVG(s.sal) AS sal
FROM hyz_emp s
HAVING AVG(s.sal) BETWEEN 1000 AND 2000
GROUP BY s.deptno, s.job
ORDER BY s.job, s.deptno;
注意事项
- where只能用于非分组函数条件
- having只能用于分组函数条件
- 部分情况可以完全替代,但是where效率更高
- ORDER BY存在优先级,如果多个列排序,那么优先级是由高到底,如代码部分执行的结果一致,排序不一致
- ORDER BY 可以使用别名也可用使用从1,2,3...对列进行排序,GROUP BY 不能使用别名,也不能使用1,2.3...对列进行分组
多表查询
- 笛卡尔积
- 等值连接
- 不等值连接
- 外连接
- 自连接
- 分层查询
笛卡尔积(法国数学家笛卡尔)
此文不具体介绍,详情见离散数学中的笛卡尔乘积问题,在数据库查询中出现此问题皆是多表时条件不足或不足以达到多表正确结果的产生
笛卡尔代码举例
SELECT COUNT(*) FROM hyz_emp s, hyz_dept o; --59994条数据,实际只有9999,即9999(hyz_emp )*6(hyz_dept )得到了59994
SELECT COUNT(*)
FROM hyz_dept o, hyz_emp s
WHERE s.deptno = o.deptno
AND s.job = o.dname; --查询结果正常,9999条数据
等值连接(内连接)
使用=条件
代码举例
SELECT s.*
FROM hyz_dept o, hyz_emp s
WHERE s.deptno = o.deptno
AND s.job = o.dname;
不等值连接
使用不等式
代码举例
SELECT s.*, o.grade
FROM hyz_emp s, hyz_salgrade o
WHERE s.sal BETWEEN o.losal AND o.hisal
ORDER BY o.grade
外连接
- 左连接(在ORACLE学习之ORACLE常见错误分析和例子
详细介绍)- 右连接(在ORACLE学习之ORACLE常见错误分析和例子
详细介绍)- 全连接(在ORACLE学习之ORACLE常见错误分析和例子
详细介绍)
代码举例
SELECT * FROM hyz_emp s WHERE s.deptno IN (50, 60); --hyz_emp没有50号的部门
SELECT * FROM hyz_dept s WHERE s.deptno IN (50, 60); --但是50号部门确实存在
SELECT s.*, '-', o.*
FROM hyz_dept o, hyz_emp s
WHERE s.deptno(+) = o.deptno
AND s.job(+) = o.dname
AND o.deptno IN (50, 60);--使用外连接后即使没有hyz_emp数据也能正常表示hyz_dept存在50和60的
注意事项
使用左连接还是右连接的关键是先查询好那个表缺另一张表对应的字段的数据就在那缺的表的对应字段加上(+),换句话就是说(+)放在缺失数据的表对应字段
自连接(自然连接)
同一个表多次连接,特殊的等值连接
代码举例
SELECT * FROM hyz_emp o WHERE o.empno = '7839'; --由于此员工既是老板又是会计,所以mgr为NULL
SELECT s.ename AS "姓名", o.ename AS "上级姓名", s.*
FROM hyz_emp o, hyz_emp s
WHERE s.mgr = o.empno; --少一条老板的数据,所以9998条数据
--等同于如下语句
SELECT o.ename AS "姓名",
(SELECT s.ename FROM hyz_emp s WHERE s.empno = o.empno) AS "上级姓名",
o.*
FROM hyz_emp o
WHERE o.mgr IS NOT NULL
ORDER BY o.mgr;
注意事项
不能直接使用数据量过大的表
可以使用分层查询代替自连接
分层查询
可以用来单表多次查询
代码举例
SELECT LEVEL AS lev, s.*
FROM hyz_emp s
CONNECT BY PRIOR s.empno = s.mgr
START WITH s.mgr IS NULL
ORDER BY lev,s.deptno
子查询
- 类型
- 单行子查询(>,<,=,<>,>=,<=...)
- 多行子查询(in,exists,any,all,.... )
代码举例
----实现员工不在深圳,的所有员工数据
--首先会先去查询深圳对应的deptno是什么,联想到此字段关联hyz_emp表
SELECT s.deptno,s.* FROM hyz_dept s WHERE s.loc = '深圳';
--查询deptno不等于40即可
SELECT * FROM hyz_emp s WHERE s.deptno != 40;
----在生产环境中,如果dname不会改变,但是修改deptno的定义,比如改成了80,那么如上的查询有需要查询两次
--所以使用等同于如下子查询,即使deptno的定义发生改变也不会影响结果
SELECT s.*
FROM hyz_emp s
WHERE EXISTS (SELECT o.deptno
FROM hyz_dept o
WHERE s.deptno = o.deptno
AND o.loc != '深圳');
慕课网有提到十个子查询注意的问题,可以参考https://www.imooc.com/learn/437
子查询注意的10个问题:
(1)子查询语法中的小括号(不可省略)
(2)子查询的书写风格(关键字大写,非关键字小写,格式化可以使用工具)
(3)可以使用子查询的位置(where,select,having,from)
(4)不可以使用子查询的位置(group by)
(5)from 后面的子查询(可以把查询sql当成一个整体的表)
(6)主查询和子查询可以不是同一张表(见子查询举例)
(7)一般不在子查询中使用排序;但在Top-N分析问题中必须对子查询排序(比如排序后才能截取一部分正确的数据)
(8)一般先执行子查询再执行主查询;但是相关子查询例外(一般情况下,为了代码清晰明了,都会使用相关子查询)
(9)单行子查询只能使用单行操作符;多行子查询只能使用多行操作符
(10)子查询中是null的问题
综合查询
代码举例
--s相关的是员工信息,o相关的是s员工的上级信息,k关联的是s的对应取k表信息
SELECT s.ename,
s.job,
s.hiredate,
s.sal,
s.comm,
s.deptno,
k.loc,
--对同一个部门求基本工资和
SUM(s.sal) over(ORDER BY s.job, s.deptno) AS sum_sal,
m.grade AS grade,
o.ename,
o.job,
o.hiredate,
o.sal,
o.comm,
o.deptno,
l.loc,
--对同一个部门求基本工资和
SUM(o.sal) over(ORDER BY o.job, o.deptno) AS sum_sal
FROM hyz_emp s
LEFT JOIN hyz_emp o
ON s.mgr = o.empno
LEFT JOIN hyz_dept k
ON s.job = k.dname
AND s.deptno = k.deptno
LEFT JOIN hyz_dept l
ON o.job = l.dname
AND o.deptno = l.deptno
JOIN hyz_salgrade m
ON s.sal BETWEEN m.losal AND m.hisal
AND m.grade BETWEEN 2 AND 3 --只取工资等级在2和3的数据
WHERE s.mgr IS NOT NULL --排除董事会及其以上的职位,他们没有上级
--AND m.grade BETWEEN 2 AND 3 --只取工资等级在2和3的数据(也可以放此位置,等同于放置JOIN...ON中)
AND EXISTS (SELECT n.ename
FROM hyz_bonus n
WHERE n.comm > 300
AND s.ename = n.ename
AND s.job = n.job) --查询存在奖金在300以上的数据
--在日期按升序排列的前提下对名字排序,优先级日期更高
ORDER BY s.hiredate, s.ename;
;