(四)MySQL基础——分组查询、链接查询、子查询

分组查询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);