MySQL加强(二)——高级查询

1.分组查询


1.1什么是分组:

针对于班上所有的同学:

分组情况1-按照性别分组:男生一组,女生一组,之后可以统计男生和女生的数量;

分组情况2-按照年龄段分组:80后一组,90后一组;

分组情况3-按照籍贯分组:广东一组,湖南一组,江西一组;

1.2语法:

SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数

FROM 表名称 [别名], [表名称 [别名] ,…]

[WHERE 条件(s)]

[GROUP BY 分组字段1 [,分组字段2 ,…]]

[ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];

注意:

  • 使用GROUP BY子句将表分成小组
  • 组函数忽略空值,可以使用ifnull
  • 结果集隐式按升序排列,如果需要改变排序方式可以使用order by 子句

1.3练习:

-- 1,按照职位分组,求出每个职位的最高和最低工资
SELECT MAX(SAL),JOB FROM emp GROUP BY JOB;

-- 2,查询出每一个部门员工的平均工资
SELECT AVG(SAL),DEPTNO FROM emp GROUP BY DEPTNO;

-- 3,查询平均工资高于2000的部门和其平均工资
SELECT AVG(SAL),DEPTNO FROM emp GROUP BY DEPTNO HAVING AVG(SAL) >=2000;

-- 4,查询各个部门和岗位的平均工资
SELECT AVG(SAL),DEPTNO,JOB FROM emp GROUP BY DEPTNO,JOB;

-- 5,查询各个管理人员下员工的平均工资,其中最低工资不能低于1300,不计算老板
SELECT AVG(SAL),DEPTNO,JOB FROM emp GROUP BY job HAVING AVG(SAL) >1300;

-- 6,查询在80,81,82,83年各进公司多少人
SELECT COUNT(*),YEAR(HIREDATE) FROM emp WHERE YEAR(HIREDATE) IN ('1980','1981','1982','1983') GROUP BY YEAR(HIREDATE);

1.4分组函数使用注意:

  • 1,出现在SELECT列表中的字段,如果出现的位置不是在分组函数中,那么必须出现在GROUP BY子句中

  • 2,在GROUP BY 子句中出现的字段,可以不出现在SELECT列表中

  • 3,如果没有GROUP BY子句,SELECT列表中的任何列或表达式不能使用统计函数(单独使用统计函数除外):

分组函数单独使用:

SELECT COUNT(empno) FROM emp;

错误的使用,出现了其他字段:

SELECT empno,COUNT(empno) FROM emp;

正确做法:

SELECT empno,COUNT(empno) FROM emp GROUP  BY empno,job;

如果现在要进行分组的话,则SELECT子句之后,只能出现分组的字段和统计函数,其他的字段不能出现:

正确做法:

SELECT job,COUNT(empno),AVG(sal)

FROM emp

GROUP BY job;

错误的做法:

SELECT deptno,job,COUNT(empno),AVG(sal)

FROM emp

GROUP BY job;

正确做法:

SELECT deptno,job,COUNT(empno),AVG(sal)

FROM emp

GROUP BY  deptno,job;

在group by 子句中,可以按单列进行分组,也可以在多列上进行分组,多列分组就是按照多个字段的组合进行分组,最终的结果也会按照分组字段进行排序显示。

1.5分组函数执行流程

查询在80,81,82,83年各进公司多少人

SELECT COUNT(empno), YEAR(hiredate) FROM emp WHERE YEAR(hiredate) IN ('1980','1981','1982','1983') GROUP BY YEAR(hiredate)

在整个语句执行的过程中,最先执行的是WHERE子句,在对表数据进行过滤后,符合条件的数据通过Group by进行分组,分组数据通过Having子句进行组函数过滤,最终的结果通过order by子句进行排序,排序的结果被返回给用户。

注意点:WHERE和HAVING的区别
WHERE:是在执行GROUP BY操作之前进行的过滤,表示从全部数据之中筛选出部分的数据,在WHERE之中不能使用统计函数;
HAVING:是在GROUP BY分组之后的再次过滤,可以在HAVING子句中使用统计函数;

image.png

2.多表查询


2.1笛卡尔积:

没有连接条件的表关系返回的结果。

多表查询会产生笛卡尔积:
假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}。

实际运行环境下,应避免使用全笛卡尔集。

select * from emp,dept
image.png

解决方案: 在WHERE加入有效的连接条件---->等值连接/不等值连接
注意:连接 n张表,至少需要 n-1个连接条件。

image.png

2.2隐式连接

没有join,通过Where的条件进行连接

2.2.1语法
SELECT [DISTINCT] * | 字段 [别名] [,字段 [别名] ,…]
FROM 表名称 [别名], [表名称 [别名] ,…]
[WHERE 条件(S)]
[ORDER BY 排序字段 [ASC|DESC] [,排序字段 [ASC|DESC] ,…]];
2.2.2隐式等值连接

使用表连接从多个表中查询数据

SELECT  table1.column, table2.column
FROM        table1, table2
WHERE   table1.column1 = table2.column2;

在 WHERE 子句中写入连接条件

当多个表中有重名列时,必须在列的名字前加上表名作为前缀

等值连接是连接操作中最常见的一种,通常是在存在主外键约束条件的多表上建立的,连接条件中的两个字段通过等号建立等值关系。

需求:查询员工编号,员工名称,员工所属部门的编号和名称.

2.2.3使用别名

使用表的别名简化了查询,提高了查询的性能

SELECT e.empno,e.ename,d.deptno,d.dname FROM emp e,dept d WHERE e.deptno = d.deptno;
2.2.4对多表做等值连接

为了连接n个表,至少需要n-1个连接条件。例如,为了连接三个表,至少需要两个连接条件

2.2.5非等值连接

查询员工的姓名,工资,所在部门的名称,以及工资的等级.
输出:

SELECT  e.ename, e.sal,d.dname,s.grade FROM emp e,dept d,salgrade s WHERE e.deptno = d.deptno AND e.sal BETWEEN s.losal AND s.hisal

2.3显示连接

隐式连接的问题在于:
1,需要在where条件中写连接条件,如果忘记写,代码不会出错,产生笛卡尔乘积;
2,隐式连接只能做内连接;

image.png
2.3.1内连接
SELECT  table1.column, table2.column
FROM    table1 JOIN table2  ON(table1.column_name = table2.column_name)
  • 自然连接的条件是基于表中所有同名列的等值连接

  • 为了设置任意的连接条件或者指定连接的列,需要使用ON子句

  • 连接条件与其它的查询条件分开书写

  • 使用ON 子句使查询语句更容易理解

  • 先执行join再执行过滤

  • 如果要先过滤再join,那么在join中可以通过join on ..and ..and来先过滤再连接

练习,使用显式内连接查询:
需求:查询员工编号,员工名称,员工所属部门的编号和名称.

SELECT e.empno,e.ename,d.deptno,d.dname  FROM emp e JOIN dept d ON  e.deptno = d.deptno

SELECT e.empno,e.ename,d.deptno,d.dname  FROM emp e JOIN dept d  USING(deptno)
2.3.2外连接

需求:查询出至少有一个员工的所有部门编号、名称,并统计出这些部门的平均工资、最低工资、最高工资。
需求:查询每一个部门的总人数.

deptno dname COUNT(empno)
10 ACCOUNTING 3
20 RESEARCH 5
30 SALES 6
40 OPERATIONS 0

外连接查询:

左外连接:查询出JOIN左边表的全部数据查询出来,JOIN右边的表不匹配的数据使用NULL来填充数据.
右外连接:查询出JOIN右边表的全部数据查询出来,JOIN左边的表不匹配的数据使用NULL来填充数据.

SELECT dname,COUNT(empno) FROM emp JOIN dept USING (deptno) GROUP BY deptno;

SELECT deptno,dname,COUNT(empno) FROM emp RIGHT JOIN dept USING (deptno) GROUP BY deptno,deptno;

左外连接
在LEFT OUTER JOIN中,会返回所有左边表中的行,即使在右边的表中没有可对应的列值。即对连接中左边的表中的记录不加限制

SELECT  table1.column, table2.column
FROM    table1 LEFT [OUTER] JOIN table2 ON (table1.column_name = table2.column_name);

右边外连接
RIGHT OUTER JOIN中会返回所有右边表中的行,即使在左边的表中没有可对应的列值。即对连接中右边的表中的记录不加限制

SELECT  table1.column, table2.column
FROM    table1 RIGHT [OUTER] JOIN table2 ON (table1.column_name = table2.column_name);

全外连接
FULL OUTER JOIN中会返回所有右边表中的行和所有左边表中的行,即使在左边的表中没有可对应的列值或者右边的表中没有可对应的列

SELECT  table1.column, table2.column
FROM    table1 FULL [OUTER] JOIN table2 ON (table1.column_name = table2.column_name);

MYSQL中暂时不支持全连接,可以通过union +左右连接来完成;

2.3.3自连接

在查询语句中,一张表可以重复使用多次,完成多次连接的需要;

需求:查询员工名称和其对应经理的名称.

SELECT e.empno,e.ename,m.ename FROM emp e LEFT JOIN emp m ON e.mgr = m.empno;

3.子查询


3.1子查询

子查询指的就是在一个查询之中嵌套了其他的若干查询.
在使用select语句查询数据时,有时候会遇到这样的情况,在where查询条件中的限制条件不是一个确定的值,而是一个来自于另一个查询的结果。

子查询一般出现在FROM和WHERE子句中.

SELECT  <select_list>
FROM table
WHERE   expr  operator
            (SELECT select_list
             FROM   table);
  • 1、子查询在主查询前执行一次
  • 2、主查询使用子查询的结果

练习
1、查询大于公司平均工资的员工姓名

SELECT ename,sal FROM emp WHERE sal >
    (SELECT AVG(sal) FROM emp)

2、查询出工资比MARTIN还要高的全部雇员信息

SELECT * FROM emp WHERE sal >
    (SELECT sal FROM emp WHERE ename = 'MARTIN')

使用子查询的注意事项:

  • 1、子查询要用括号括起来
  • 2、将子查询放在比较运算符的右边(增强可读性)
  • 3、对单行子查询使用单行运算符
  • 4、对多行子查询使用多行运算符

3.2子查询种类

3.2.1单行单列子查询:

只包含一个字段的查询,返回的查询结果也只包含一行数据
1、返回一行记录
2、使用单行记录比较运算符:=;>;>=;<;<=;<>

练习:
1、查询大于公司平均工资的员工姓名

SELECT ename,sal FROM emp WHERE sal >
    (SELECT AVG(sal) FROM emp)

2、查询出工资比MARTIN还要高的全部雇员信息

SELECT * FROM emp WHERE sal >
    (SELECT sal FROM emp WHERE ename = 'MARTIN')
3.2.2多行单列子查询:

只包含了一个字段,但返回的查询结果可能多行或者零行(多行子查询返回多行单列)
1、返回多行
2、使用多行比较运算符
IN:与列表中的任意一个值相等 :需求:查询工资等于部门经理的员工信息.
ANY:与子查询返回的任意一个值比较
1): = ANY:此时和IN操作符相同. :需求:查询工资等于任意部门经理的员工信息.
2): > ANY:大于子查询中最小的数据. :需求:查询工资大于任意部门经理的员工信息.
3): < ANY:大于子查询中最大的数据. :需求:查询工资小于任意部门经理的员工信息.
ALL:与子查询返回的每一个值比较
1): > ALL:大于子查询中最大的数据.
2): < ALL:小于子查询中最小的数据.

练习
查询平均工资高于公司平均工资的部门信息

SELECT deptno,AVG(sal) FROM  dept JOIN emp USING (deptno) GROUP BY deptno HAVING AVG(sal) > 
(
    SELECT AVG(sal) FROM emp
);


select * from departments where department_id in 
(select department_id from employees 
    group by department_id having avg(salary)>
        (select avg(salary) from employees));
3.2.3多列子查询:

包含多个字段的返回,查询结构可能是单行或者多行。(子查询返回的结果是多行多列)

一般会把子查询返回的结果当成一个临时表,接着在临时表上继续查询或者连接查询;
注意,多行多列的子查询返回的结果必须要设置一个临时表名;

查询出每个部门的编号、名称、部门人数、平均工资:

SELECT d.deptno,d.dname,COUNT(e.empno),IFNULL(AVG(e.sal),0)
FROM dept d JOIN emp e USING (deptno)
GROUP BY d.deptno,d.dname

分析性能:笛卡尔积数量:

可以先把每一个部门的编号,总人数,平均工资先查询出来.

SELECT deptno dno,COUNT(empno) count ,AVG(sal) avg  FROM emp GROUP BY dno

再和dept表联合查询部门名称.

SELECT  dept.deptno,temp.count,temp.avg  FROM dept JOIN (SELECT deptno dno,COUNT(empno) count ,AVG(sal) avg  FROM emp GROUP BY deptno) temp ON dept.deptno = temp.dno
image.png

3.3UNION/UNION ALL

JOIN是用于把表横向连接,UNION/UNION ALL是用于把表纵向连接(一般用于做查询的临时表)
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。

3.3.1注意

1,UNION 内部的 SELECT 语句必须拥有相同数量的列。
2,列也必须拥有兼容的数据类型。
3,每条 SELECT 语句中的列的顺序必须相同。
4,UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名
5,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL(性能高).

3.3.2语法:
SELECT column_name(s) FROM table_name1
UNION|UNION ALL
SELECT column_name(s) FROM table_name2
3.3.3在MYSQL 中实现FULL JOIN:

查询员工的编号,名称和部门名称.

1:先在emp表中插入一条数据,并设置depto为NULL.
2:查询

SELECT empno,ename,dname FROM emp LEFT JOIN dept USING (deptno)
UNION
SELECT empno,ename,dname FROM emp RIGHT JOIN dept USING (deptno)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容