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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Steriles_/article/details/82767280

分组查询

GROUP BY语法:

  SELECT  分组函数  ,列(要求出现在GROUP BY的后面)FROM  表  【WHERE  筛选条件】GROUP BY  分组的列表

【ORDER  BY  子句】

注意:

查询列表比较特殊,要求是分组函数和GROUP BY 后出现的字段

分类:

分组查询中的筛选条件分为两类:分组前筛选分组后筛选。两类筛选的数据源是不一样的,前者对原始表进行筛选后者对分组后的结果集进行筛选;因此这些筛选条件出现的位置也不一样,前者在GROUP BY子句的前面后者在GROUP BY 子句的后面;并且用到的关键字也不一样,前者用的是WHERE,而后者用到了HAVING这一关键字。

分组函数做条件,肯定是放在HAVING子句中。

能用分组前筛选的,就优先考虑使用分组前筛选。

GROUP BY子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开,没有顺序要求),表达式或函数(用的较少),也可以添加排序(排序放在整个分组查询的最后

扫描二维码关注公众号,回复: 3281934 查看本文章

按函数分组查询的一些例子:

-- 案例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);

猜你喜欢

转载自blog.csdn.net/Steriles_/article/details/82767280