分组查询
GROUP BY语法:
SELECT 分组函数 ,列(要求出现在GROUP BY的后面)FROM 表 【WHERE 筛选条件】GROUP BY 分组的列表
【ORDER BY 子句】
注意:
查询列表比较特殊,要求是分组函数和GROUP BY 后出现的字段
分类:
分组查询中的筛选条件分为两类:分组前筛选 和 分组后筛选。两类筛选的数据源是不一样的,前者对原始表进行筛选,后者对分组后的结果集进行筛选;因此这些筛选条件出现的位置也不一样,前者在GROUP BY子句的前面,后者在GROUP BY 子句的后面;并且用到的关键字也不一样,前者用的是WHERE,而后者用到了HAVING这一关键字。
分组函数做条件,肯定是放在HAVING子句中。
能用分组前筛选的,就优先考虑使用分组前筛选。
GROUP BY子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开,没有顺序要求),表达式或函数(用的较少),也可以添加排序(排序放在整个分组查询的最后)
按函数分组查询的一些例子:
-- 案例1,:查询每个工种的最高工资 【每个】
SELECT MAX(salary),job_id FROM employees GROUP BY job_id;
-- 案例2:查询每个位置上的部门个数 【每个】
SELECT COUNT(*),location_id FROM departments GROUP BY location_id;
-- 案例3:查询邮箱中包含a字符的,每个部门的平均工资 【每个】
SELECT AVG(salary),department_id FROM employees WHERE email LIKE '%a%' GROUP BY department_id;
-- 案例4:查询有奖金的每个领导手下员工的最高工资 【添加分组前的筛选】
SELECT MAX(salary), manager_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY manager_id;
-- 案例5:查询哪个部门的员工数>2 【添加分组后的筛选】
-- ①查询每个部门的员工个数
SELECT COUNT(*),department_id FROM employees GROUP BY department_id
-- ②根据①的结果进行筛选,查询哪个部门的员工个数>2
SELECT COUNT(*),department_id FROM employees GROUP BY department_id HAVING COUNT(*)>2;
GROUP BY 和 HAVING 后面是支持放别名的,但是WHERE后面不支持,且这些功能在Oracle中也是有限制的,在MySQL中才这样处理。
按多个字段分组查询的一些例子:
-- 案例1、查询每个部门每个工种的员工的平均工资
SELECT department_id,AVG(salary),job_id FROM employees GROUP BY department_id,job_id;
连接查询
含义:
连接查询又叫多表查询,当查询的字段来自于多个表时,就会用到连接查询
笛卡尔乘积现象:表1有m行,表2有n行,结果为m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件
分类:
1、按年代分类:
sql92标准:MySQL仅仅支持内连接
sql99标准【推荐使用】:支持内连接+外连接(MySQL仅支持左外连接+右外连接)+交叉连接
2、按功能分类:
内连接:等值连接、非等值连接、自连接
外连接:左外连接、右外连接、全外连接
交叉连接
一、sql92标准
(1)等值连接的特点:
多表等值连接的结果为多表的交集部分;N表连接,至少需要N-1个连接条件;多表的顺序没有要求;一般需要为表取别名;可以搭配前面介绍的所有查询子句使用,比如排序、分组、筛选。
-- 案例1、查询员工名和对应的部门名 【等值连接】
SELECT last_name,department_name FROM employees,departments WHERE departments.department_id=employees.department_id;
-- 案例2、查询有奖金的员工名、部门名 【等值连接+筛选】
SELECT last_name,department_name,commission_pct FROM employees,departments WHERE employees.commission_pct IS NOT NULL AND empartments.department_id = employees.department_id;
-- 案例3、查询每个城市的部门个数 【等值连接+分组】
SELECT COUNT(*),city FROM departments,locations WHERE department.location_id=locations.location_id GROUP BY city;
-- 案例4、查询每个工种的工种名和员工的个数,并且按员工个数降序 【等值连接+排序】
SELECT COUNT(*),job_title FROM employees,jobs WHERE employees.job_id=jobs.job_id GROUP BY job_title ORDER BY COUNT(*) DESC;
为表起别名:可以提高语句的简洁度,区分多个重名的字段。如果为表起了别名,则查询的字段不能使用原来的表名去限定了。
(2)非等值连接:
-- 案例1、查询员工的工资和工资级别 【非等值连接+筛选】
SELECT salary,grade_level FROM employees e ,job_gardes g WHERE salary BETWEEN g.lowest_sal AND g.highest_sal AND g.grade_level='A';
(3)自连接:
-- 案例1、查询员工名和上级的名称 【自连接】
SELECT e.employee_id,e.last_name,m.employee_id,m.last_name FROM employees e ,employees m WHERE e.manager_id=m.employee_id;
二、sql99标准
语法:
SELECET 查询列表 FROM 表1 别名 【连接类型】 JOIN 表2 别名 ON 连接条件 WHERE 筛选条件 GROUP BY 分组 HAVING 筛选条件 ORDER BY 排序列表;
原来表1 和表2 之间的逗号‘,’变成了连接类型,原来的连接条件用WHERE连接,现在替换成了ON连接,原来接筛选条件用AND连接,现在用WHERE连接。
对于【连接类型】而言,可以分为:
内连接:INNER
外连接:左外连接 :LEFT (OUTER 可省略) ;右外连接:RIGHT (OUTER 可省略) ;全外连接:FULL (OUTER 可省略)
交叉连接:CROSS
(1)内连接:
等值连接:
特点:
1、可以添加排序、分组、筛选;
INNER其实是可以省略的;
2、筛选条件放在WHERE后面,连接条件放在ON后,提高分离性,便于阅读;
3、INNER JOIN 连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集。
-- 案例1、查询员工名、部门名 【等值查询】
SELECT last_name,department_name FROM employees e INNER JOIN departments d ON e.department_id=d.department_id;
-- 案例2、查询名字中包含e的员工名和工种名 【等值查询+筛选】
SELECT last_name,job_title FROM employees e INNER JOIN jobs j ON e.job_id=j.job_id WHERE e.last_name LIKE '%e%';
-- 案例3、查询部门个数大于3的城市名和部门个数 【等值查询+筛选+分组】
SELECT COUNT(*),city FROM locations l INNER JOIN departments d ON l.location_id=d.location_id GROUP BY city HAVING COUNT(*)>3;
非等值连接:
-- 案例1、查询员工的工资级别 【非等值查询】
SELECT salary,grade_level FROM employees e INNER JOIN job_grades g ON e.salary BETWEEN g.lowest_sal AND g.highest_sal;
自连接:
-- 案例1、查询员工的名字、上级的名字
SELECT e.last_name,m.last_name FROM employees e INNER JOIN employees m ON e.manager_id=m.employee_id;
(2)外连接:
应用场景:用于查询一个表中有,另一个表中没有的记录
特点:
1、外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值,若没有则显示NULL。
外连接查询结果 = 内连接查询结果 + 主表中有而从表中没有的记录
2、左外连接,LEFT JOIN 左边的是主表;右外连接,RIGHT JOIN右边的是主表。
3、左外和右外交换两个表的顺序,可以实现同样的效果。
-- 案例1、查询男朋友不在男生表中的女生名 【左外连接】
SELECT b.name,bo.* FROM beauty b LEFT OUTER JOIN boys bo ON b.boyfriend_id=bo.id WHERE bo.id IS NULL;
-- 案例2、查询哪个部门没有员工 【左外连接】
SELECT e.employee_id,d.* FROM departments d LEFT OUTER JOIN employees e ON e.department_id=d.department_id WHERE e.employee_id IS NULL;
-- 案例2、查询哪个部门没有员工 【右外连接】
SELECT e.employee_id,d.* FROM employees e LEFT OUTER JOIN departments d ON e.department_id=d.department_id WHERE e.employee_id IS NULL;
全外连接MySQL是不支持的,这里概述一下:
全外连接 = 内连接查询结果 + 表1中有但表2中没有的 + 表2中有但表1中没有的
如果说只想想查询 “表1中有但表2中没有” 以及 “表2中有但表1中没有 这两部分”,只需要在WHERE后面加上某个表中的主键 IS NULL 就行了
(3)交叉连接:就相当于笛卡尔乘积。
-- 交叉连接
SELECT b.*,bo.* FROM beauty b CROSS JOIN boys bo;
sql92和sql99的对比:
功能:sql99支持的功能较多;
可读性:sql99实现连接条件和筛选条件的分离,可读性较高。
1、内连接
SELECT <select_list> FROM A INNER JOIN B ON A.key=B.key;
2、外连接——左外连接
SELECT <select_list> FROM A LEFT JOIN B ON A.key=B.key;
3、外连接——右外连接
SELECT <select_list> FROM A RIGHT JOIN B ON A.key=B.key;
4、外连接——左外连接的基础上去掉一部分
SELECT <select_list> FROM A LEFT JOIN B ON A.key=B.key WHERE B.key IS NULL;
5、外连接——右外连接的基础上去掉一部分
SELECT <select_list> FROM A RIGHT JOIN B ON A.key=B.key WHERE A.key IS NULL;
6、全外连接
SELECT <select_list> FROM A FULL JOIN B ON A.key=B.key;
7、全外连接的基础上去掉一部分
SELECT <select_list> FROM A FULL JOIN B ON A.key=B.key WHERE A.key IS NULL OR B.key IS NULL;
子查询
含义:
出现在其他语句中的SELECT语句,称为子查询或内查询。
而外部出现的查询语句称为主查询或外查询。.
分类:
按子查询出现的位置分:
SELECT 后面:仅仅支持标量子查询
FROM 后面:支持表子查询
WHERE或HAVING 后面:支持标量子查询(单行)、 列子查询(多行),行子查询用的较少
EXISTS 后面(相关子查询):支持表子查询
按结果集的行列数不同分:
标量子查询(结果集只有一行一列)
列子查询 / 多行子查询(结果集只有一列多行)
行子查询(结果集有一行多列)
表子查询(结果集一般为多行多列,其范围大,包括上述说的不同行列数的子查询)
WHERE或HAVING后面:
1、标量子查询(单行子查询)
2、列子查询(多行子查询)
3、行子查询(结果集一行多列或者多行多列)
特点:
(1)子查询放在小括号内
(2)子查询一般放在条件的右侧
(3)标量子查询一般单配单行操作符使用,单行操作符号有:> 、< 、>= 、<= 、= 、<>
(4)列子查询一半搭配多行操作符使用,多行操作符包括:IN(等于列表中的任意一个) 、ANY / SOME (和子查询返回的某一个值比较)、ALL(和子查询返回的所有值比较)
-- 案例1、谁的工资比Abel高 【标量子查询】
SELECT salary FROM employees WHERE last_name='Abel';
SELECT * FROM employees WHERE salary > (SELECT salary FROM employees WHERE last_name='Abel');
-- 案例2、返回公司工资最少的员工的last_name,job_id和salary 【标量子查询】
SELECT MIN(salary) FROM employees;
SELECT last_name,job_id,salary FROM employees WHERE salary = (SELECT MIN(salary) FROM employees;)
-- 案例3、返回location_id是1400或1700的部门中的所有员工姓名 【列子查询】
SELECT department_id FROM departments WHERE location_id IN (1400,1700);
SELECT last_name FROM employees WHERE department_id IN(SELECT DISTINCT department_id FROM departments WHERE location_id IN (1400,1700))
-- 案例4、返回其他工种中比job_id为'IT_PROG'部门任一工资低的员工的员工号、姓名、job_id以及salary 【列子查询】
SELECT salary FROM employees WHERE job_id='IT_PROG';
SELECT employee_id,last_name,job_id,salary FROM employees WHERE salary < ANY(SELECT salary FROM employees WHERE job_id='IT_PROG');
SELECT employee_id,last_name,job_id,salary FROM employees WHERE salary < (SELECT salary FROM employees WHERE job_id='IT_PROG') AND job_id<>'IT_PROG';
-- 案例5、返回其他工种中比job_id为'IT_PROG'部门所有工资低的员工的员工号、姓名、job_id以及salary 【列子查询】
SELECT employee_id,last_name,job_id,salary FROM employees WHERE salary < ALL(SELECT salary FROM employees WHERE job_id='IT_PROG');
SELECT employee_id,last_name,job_id,salary FROM employees WHERE salary < (SELECT MIN(salary) FROM employees WHERE job_id='IT_PROG');
-- 案例6、查询员工编号最小并且工资最高的员工信息 【行子查询】
SELECT MIN(employee_id) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT * FROM employees WHERE employee_id=(SELECT MIN(employee_id) FROM employees) AND salary=(SELECT MAX(salary) FROM employees);
SELECT * FROM employees WHERE (employee_id,salary)=(SELECT MIN(employee_id) , MAX(salary) FROM employees);
SELECT后面:仅仅支持标量子查询
-- 案例1、查询每个部门的员工个数 【标量子查询】
SELECT d.* ,( SELECT COUNT(*) FROM employees e WHERE e.department_id=d.department_id) FROM departments d;
FROM后面:将子查询结果充当一张表,要求必须起别名
-- 案例1、查询每个部门的平均工资的工资等级
SELECT AVG(salary),departmrnt_id FROM employees GROUP BY department_id;
SELECT ag_dep.*,g.grade_level FROM (SELECT AVG(salary),departmrnt_id FROM employees GROUP BY department_id) ag_dep INNER JOIN job_grades g ON ag_dep.ag BETWEEN lowers_sal AND highest_sal;
EXISTS后面:相关子查询(用的比较少)
语法:
EXISTS (完整的查询语句)
结果:1或0
-- 是否存在,布尔类型 查询结果为1或0
SELECT EXISTS (SELECT employee_id FROM employees);
-- 案例1、查询有员工的部门名
SELECT department_name FROM departments WHERE EXISTS (SELECT * FROM employees e WHERE d.department_id=e.department_id);