分组查询sql
GROUP BY语法:函数
SELECT 分组函数 ,列(要求出如今GROUP BY的后面)FROM 表 【WHERE 筛选条件】GROUP BY 分组的列表spa
【ORDER BY 子句】code
注意:blog
查询列表比较特殊,要求是分组函数和GROUP BY 后出现的字段排序
分类:ci
分组查询中的筛选条件分为两类:分组前筛选 和 分组后筛选。两类筛选的数据源是不同的,前者对原始表进行筛选,后者对分组后的结果集进行筛选;所以这些筛选条件出现的位置也不同,前者在GROUP BY子句的前面,后者在GROUP BY 子句的后面;而且用到的关键字也不同,前者用的是WHERE,而后者用到了HAVING这一关键字。it
分组函数作条件,确定是放在HAVING子句中。io
能用分组前筛选的,就优先考虑使用分组前筛选。ast
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中才这样处理。
按多个字段分组查询的一些例子:
-- 案例一、查询每一个部门每一个工种的员工的平均工资 SELECT department_id,AVG(salary),job_id FROM employees GROUP BY department_id,job_id;
链接查询
含义:
链接查询又叫多表查询,当查询的字段来自于多个表时,就会用到链接查询
笛卡尔乘积现象:表1有m行,表2有n行,结果为m*n行
发生缘由:没有有效的链接条件
如何避免:添加有效的链接条件
分类:
一、按年代分类:
sql92标准:MySQL仅仅支持内链接
sql99标准【推荐使用】:支持内链接+外链接(MySQL仅支持左外链接+右外链接)+交叉链接
二、按功能分类:
内链接:等值链接、非等值链接、自链接
外链接:左外链接、右外链接、全外链接
交叉链接
1、sql92标准
(1)等值链接的特色:
多表等值链接的结果为多表的交集部分;N表链接,至少须要N-1个链接条件;多表的顺序没有要求;通常须要为表取别名;能够搭配前面介绍的全部查询子句使用,好比排序、分组、筛选。
-- 案例一、查询员工名和对应的部门名 【等值链接】 SELECT last_name,department_name FROM employees,departments WHERE departments.department_id=employees.department_id; -- 案例二、查询有奖金的员工名、部门名 【等值链接+筛选】 SELECT last_name,department_name,commission_pct FROM employees,departments WHERE employees.commission_pct IS NOT NULL AND empartments.department_id = employees.department_id; -- 案例三、查询每一个城市的部门个数 【等值链接+分组】 SELECT COUNT(*),city FROM departments,locations WHERE department.location_id=locations.location_id GROUP BY city; -- 案例四、查询每一个工种的工种名和员工的个数,而且按员工个数降序 【等值链接+排序】 SELECT COUNT(*),job_title FROM employees,jobs WHERE employees.job_id=jobs.job_id GROUP BY job_title ORDER BY COUNT(*) DESC;
为表起别名:能够提升语句的简洁度,区分多个重名的字段。若是为表起了别名,则查询的字段不能使用原来的表名去限定了。
(2)非等值链接:
-- 案例一、查询员工的工资和工资级别 【非等值链接+筛选】 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)自链接:
-- 案例一、查询员工名和上级的名称 【自链接】 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;
2、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)内链接:
等值链接:
特色:
一、能够添加排序、分组、筛选;
INNER实际上是能够省略的;
二、筛选条件放在WHERE后面,链接条件放在ON后,提升分离性,便于阅读;
三、INNER JOIN 链接和sql92语法中的等值链接效果是同样的,都是查询多表的交集。
-- 案例一、查询员工名、部门名 【等值查询】 SELECT last_name,department_name FROM employees e INNER JOIN departments d ON e.department_id=d.department_id; -- 案例二、查询名字中包含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的城市名和部门个数 【等值查询+筛选+分组】 SELECT COUNT(*),city FROM locations l INNER JOIN departments d ON l.location_id=d.location_id GROUP BY city HAVING COUNT(*)>3;
非等值链接:
-- 案例一、查询员工的工资级别 【非等值查询】 SELECT salary,grade_level FROM employees e INNER JOIN job_grades g ON e.salary BETWEEN g.lowest_sal AND g.highest_sal;
自链接:
-- 案例一、查询员工的名字、上级的名字 SELECT e.last_name,m.last_name FROM employees e INNER JOIN employees m ON e.manager_id=m.employee_id;
(2)外链接:
应用场景:用于查询一个表中有,另外一个表中没有的记录
特色:
一、外链接的查询结果为主表中的全部记录,若是从表中有和它匹配的,则显示匹配的值,若没有则显示NULL。
外链接查询结果 = 内链接查询结果 + 主表中有而从表中没有的记录
二、左外链接,LEFT JOIN 左边的是主表;右外链接,RIGHT JOIN右边的是主表。
三、左外和右外交换两个表的顺序,能够实现一样的效果。
-- 案例一、查询男友不在男生表中的女生名 【左外链接】 SELECT b.name,bo.* FROM beauty b LEFT OUTER JOIN boys bo ON b.boyfriend_id=bo.id WHERE bo.id IS NULL; -- 案例二、查询哪一个部门没有员工 【左外链接】 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; -- 案例二、查询哪一个部门没有员工 【右外链接】 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实现链接条件和筛选条件的分离,可读性较高。
一、内链接
SELECT <select_list> FROM A INNER JOIN B ON A.key=B.key;
二、外链接——左外链接
SELECT <select_list> FROM A LEFT JOIN B ON A.key=B.key;
三、外链接——右外链接
SELECT <select_list> FROM A RIGHT JOIN B ON A.key=B.key;
四、外链接——左外链接的基础上去掉一部分
SELECT <select_list> FROM A LEFT JOIN B ON A.key=B.key WHERE B.key IS NULL;
五、外链接——右外链接的基础上去掉一部分
SELECT <select_list> FROM A RIGHT JOIN B ON A.key=B.key WHERE A.key IS NULL;
六、全外链接
SELECT <select_list> FROM A FULL JOIN B ON A.key=B.key;
七、全外链接的基础上去掉一部分
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)标量子查询通常单配单行操做符使用,单行操做符号有:> 、< 、>= 、<= 、= 、<>
(4)列子查询一半搭配多行操做符使用,多行操做符包括:IN(等于列表中的任意一个) 、ANY / SOME (和子查询返回的某一个值比较)、ALL(和子查询返回的全部值比较)
-- 案例一、谁的工资比Abel高 【标量子查询】 SELECT salary FROM employees WHERE last_name='Abel'; SELECT * FROM employees WHERE salary > (SELECT salary FROM employees WHERE last_name='Abel'); -- 案例二、返回公司工资最少的员工的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;) -- 案例三、返回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)) -- 案例四、返回其余工种中比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'; -- 案例五、返回其余工种中比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'); -- 案例六、查询员工编号最小而且工资最高的员工信息 【行子查询】 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后面:仅仅支持标量子查询
-- 案例一、查询每一个部门的员工个数 【标量子查询】 SELECT d.* ,( SELECT COUNT(*) FROM employees e WHERE e.department_id=d.department_id) FROM departments d;
FROM后面:将子查询结果充当一张表,要求必须起别名
-- 案例一、查询每一个部门的平均工资的工资等级 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); -- 案例一、查询有员工的部门名 SELECT department_name FROM departments WHERE EXISTS (SELECT * FROM employees e WHERE d.department_id=e.department_id);