09_MySQL笔记-组函数-GROUP BY-LIMIT-多表连接查询-子查询-UNION-索引-视图-存储过程

Posted mycpen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了09_MySQL笔记-组函数-GROUP BY-LIMIT-多表连接查询-子查询-UNION-索引-视图-存储过程相关的知识,希望对你有一定的参考价值。

文章目录


个人博客
https://blog.csdn.net/cPen_web


组函数

组函数

	组函数又叫做聚集函数(aggregation function),它在一个行的集合(一组行)上进行操作,对每个组给一个结果

常用的组函数
	AVG([DISTINCT] expr)  :求平均值
	COUNT(*|[DISTINCT]  expr) :统计行的数量
	MAX([DISTINCT] expr)  :求最大值
	MIN([DISTINCT] expr)  :求最小值
	SUM([DISTINCT] expr)  :求累加和

默认情况下,组函数忽略列值为null的行,不把它们拿来参与计算
count(*):返回表中满足where条件的行的数量

例1:有多少球员住在Stratford?
 SELECT  count(*)
  FROM  players
  WHERE  town='Stratford'; 
count():返回列值非空的行的数量

例3:一共有多少个联盟会员号码?
SELECT  count(leagueno)
  FROM  players;
count(distinct):返回列值非空的、并且列值不重复的行的数量

例4:查询球员所居住的城市的数量
SELECT  count(DISTINCT town)
  FROM  players;
组函数的参数也可以是函数表达式

例5:得到出现在penalties表中的不同年份的数量
SELECT count(DISTINCT year(payment_date))
  FROM penalties;
一个SELECT子句中可出现多个聚集函数

例6:得到球员居住城市的数量和性别的数量
SELECT count(DISTINCT town),count(DISTINCT sex)
  FROM players;
MAX和MIN

例7:得到最高和最低的罚款金额
SELECT  max(amount), min(amount)
  FROM  penalties; 
如果统计的列中只有NULL值,那么MAX和MIN就返回NULL
SUM和AVG函数

例9:得到44球员罚款的总额以及平均值
SELECT  sum(amount), avg(amount)
  FROM  penalties
 WHERE  playerno=44;
10:计算公司的平均佣金。要求拿佣金的雇员才参与计算
SELECT avg(commission_pct) 
  FROM employees;
11:计算公司的平均佣金。要求所有的雇员都参与计算
SELECT avg(ifnull(commission_pct,0)) 
  FROM employees; 

要想列值为NULL的行也参与组函数的计算,必须使用IFNULL函数对NULL值做转换

GROUP BY

GROUP BY子句
	原则:只有在group by后面出现过的字段,才能在select后面出现;没有出现的 使用聚合函数
	group by子句后面进行条件过滤的话,使用having子句   (having一般搭配group by)

	GROUP BY 子句根据给定列或者表达式的每一个不同的值将表中的行分成不同的组。使用组函数返回每一组的统计信息
指定一个列进行分组

例1:查询每个城市的名称和球员的数量
SELECT  town, count(*)
  FROM  players
  GROUP BY  town; 

加条件:找出 城市超过三个人的城市
SELECT  town, count(*)  FROM  players  GROUP BY  town  HAVING COUNT(town) > 3;
2:对于每个球队,得到编号、参加比赛的数量以及赢得的局数
SELECT  teamno,count(*), sum(won)
  FROM  matches
  GROUP BY  teamno;
使用多个分组列,形成“大组中再分小组”的分组效果

例3:统计每个球队中每个球员所赢得的总局数
SELECT teamno, playerno, sum(won)
  FROM  matches
  GROUP BY  teamno, playerno;
根据表达式分组

例4:对于penalties表中的每一年,得到支付罚款的次数
SELECT  year(payment_date), count(*)
  FROM  penalties
  GROUP BY  year(payment_date);  
带有排序的分组
	如果分组列和排序列相同,则可以合并group byorder by子句

例6:得到每个球队的编号和比赛总场数,结果按球队编号降序排序
SELECT  teamno, count(*)
  FROM  matches
  GROUP BY  teamno
  ORDER BY  teamno DESC; 

可以把desc(或者asc)包含到group by子句中简化
SELECT  teamno, count(*)
  FROM  matches
  GROUP BY  teamno DESC;
GROUP BY子句的规则

	1 .出现在SELECT子句中的单独的列,必须出现在GROUP BY子句中作为分组列。这条规则适用于其它数据库,但是不适用于mysql

	在MYSQL中执行,不抛出异常,而是返回结果。其中town列的值随机返回。通过设置sql_mode系统变量的值为ONLY_FULL_GROUP_BY来强制mysql和其它数据库一样,遵循该规则(推荐)

	原则:只有在group by后面出现过的字段,才能在select后面出现;没有出现的 使用聚合函数

GROUP_CONCAT()函数

	MYSQL特有的组函数。该函数的值等于属于一个组的指定列的所有值。这些值一个挨一个的放置,以逗号隔开,并且以字符串表示

例8:对于每个球队,得到其编号和所有球员的编号
SELECT  teamno, group_concat(playerno)			#注:group_concat(distinct playerno)  去重
  FROM  matches
  GROUP BY  teamno; 
查看出同在一个城市的球员的名字和城市名、数量 --> 家乡(town) 相同的球员,查询出家乡名和所以的球员的名字
select town,GROUP_CONCAT(name),COUNT(NAME) from PLAYERS GROUP BY TOWN
CONCAT()函数		字符创的拼接

root@test mysql>select CONCAT(name,sex,town) from PLAYERS;
root@test mysql>select CONCAT_WS('#',name,sex,town) from PLAYERS;		#注:指定分隔符

Linux	cat命令  就是concat的缩写

对分组结果进行过滤

不能使用WHERE子句对分组后的结果进行过滤
不能在WHERE子句中使用组函数
因为WHERE子句比GROUP BY先执行,而组函数必须在分完组之后才执行
HAVING子句

	专门用来对分组后的结果进行过滤
	HAVING可以单独使用而不和GROUP BY配合
	HAVING子句中可以使用组函数
1:得到那些多于一次罚款的球员的编号
SELECT  playerno
  FROM  penalties
  GROUP BY  playerno
  HAVING  count(*) > 1;
2:对于罚款总额大于150元的球员,得到编号和罚款额
SELECT  playerno, sum(amount)
  FROM  penalties
  GROUP BY  playerno
  HAVING  sum(amount) > 150;
如果只有HAVING子句而没有GROUP BY,表中所有的行分为一组 

例3:得到所有罚款的总和,要求总和大于250元时才显示
SELECT sum(amount)
  FROM penalties
  HAVING sum(amount) > 250; 
HAVING子句中的列,要么出现在一个组函数中,要么出现在GROUP BY子句中。否则出错

#注:having子句	专门给group by 做条件过滤的,是一种组合

LIMIT

LIMIT子句

	MySQL特有的子句。它是SELECT语句中的最后一个子句(在order by后面)。它用来表示从结果集中选取最前面或最后面的几行。偏移量offset的最小值为0

	语法		limit  <获取的行数> [OFFSET <跳过的行数>]
	或者		limit [<跳过的行数>,] <获取的行数>  
取第3--6行
	limit 2,4
取前3limit 3
只取第3limit 2,1
1:得到编号最大的前4个球员的编号和名字
SELECT  playerno, name
  FROM  players
  ORDER BY  playerno DESC
  LIMIT  4;
带偏移量的limit
	用来跳过前面的几行后再取

例4:得到球员编号最低的5个球员的编号和名字,从第4个球员开始
SELECT playerno,  name
  FROM players
  ORDER BY playerno ASC
  LIMIT  3, 5;  -- 或者limit 5 offset 3;
6:第3高的罚款额是多少?
SELECT  DISTINCT  amount
          FROM PENALTIES
      ORDER BY amount DESC
       LIMIT 2,1

多表连接查询

多表连接查询
	从多个表获得数据
	多表连接查询,会消耗大量性能,多表 合并与排序,大量计算,消耗内存、CPU等
连接类型

连接(join):将一张表中的行按照某个条件(连接条件)和另一张表中的行连接起来形成一个新行的过程叫做连接

根据查询返回的结果,连接可以分为3大类:
	内连接(inner join)					#注:符合条件的行	显示
	外连接(outer join)					#注:一张表里面 符合条件的行显示,另外一张表里面不符合条件的行也显示
	交叉连接(cross join)

在连接查询中,一个列可能出现在多张表中。为了避免引起歧义,通常在列名前面加上表名或表别名作为前缀

使用表别名作为前缀,可以使得SQL代码较短,使用的内存更少
内连接
外连接
	左连接
	右连接
交叉连接
内连接(inner join)
	只返回两张表中所有满足连接条件的行

1 .使用using子句
2 .使用on子句
3 .使用where子句
使用using子句

例2:
SELECT  *
  FROM  penalties 
  JOIN  teams
  USING(playerno);						#注:playerno		两张表里都有的公共字段
 可见,连接列在结果集中只出现一次 

SELECT NAME,BIRTH_DATE,AMOUNT,PLAYERNO FROM PLAYERS JOIN PENALTIES USING(PLAYERNO);
使用ON子句

例3:查询每个球队的编号以及队长的名字
SELECT  t.teamno, p.name
  FROM  teams t
  JOIN  players p
  ON  t.playerno = p.playerno;
传统的连接写法

在FROM子句中列出所有要连接的表的名字,以逗号分隔。连接条件写在WHERE子句中

例4:对于每笔罚款,找出罚款编号,金额以及引起罚款的球员编号和姓名
SELECT  paymentno, pen.playerno, amount,NAME
  FROM  penalties pen, players p
  WHERE  pen.playerno = p.playerno;

另外2种写法:
SELECT paymentno,pen.playerno,amount,name FROM PENALTIES pen 
JOIN PLAYERS p 
USING(playerno)

SELECT paymentno,pen.playerno,amount,name FROM PENALTIES pen 
JOIN PLAYERS p 
ON pen.playerno = p.playerno

注意:一旦给表定义了别名,那么原始的表名就不能在出现在该语句的其它子句中了
3表连接查询

例5:查询每场比赛的编号、球员编号、球队编号、球员的名字以及球队的分级
SELECT  m.matchno,  m.playerno,  m.teamno, p.name,  t.division
  FROM matches m, players p, teams t
  WHERE  m.playerno = p.playerno
        AND  m.teamno  =  t.teamno;

表:比赛表 matches    PLAYERS    TEAMS
字段:比赛的编号、球员编号、球队编号、球员的名字以及球队的分级
条件:PLAYERNO

SELECT MATCHNO,m.PLAYERNO,m.TEAMNO,NAME,division from MATCHES m,PLAYERS p,TEAMS t
where t.teamno = m.teamno and m.playerno = p.playerno
外连接
	不满足条件的行,也可以显示出来
	左外连接
	右外连接
左外连接
	#注:不满足条件的行也显示出来,以左表为准
	除了返回两张表中所有满足连接条件的行之外,还要返回左表中所有不满足连接条件的行。所谓左表,就是写在LEFT JOIN关键字左边的表

例6:查询所有球员的编号、姓名和他引起的罚款。没有罚款的球员也要显示
SELECT  p.playerno, p.name, pen.amount
  FROM players p
  LEFT JOIN  penalties pen
  ON p.playerno = pen.playerno; 
右外连接
	#注:不满足条件的行也显示出来,以右表为准
	除了返回两张表中所有满足连接条件的行之外,还要返回右表中所有不满足连接条件的行。所谓右表,就是写在RIGHT  JOIN关键字右边的表

例7:查询所有球员的编号、姓名和他们是队长的球队的编号。要求没有当队长的球员也要显示
SELECT  p.playerno, p.name, t.teamno
  FROM  teams t
  RIGHT JOIN  players p
  ON t.playerno = p.playerno;

#注:可以改成左外连接,是一样的

MySQL不支持全外连接
交叉连接
	--> 笛卡尔积

笛卡尔积
	如果连接查询没有写任何连接条件,那么一张表中的所有行可以和另一张表中的所有行进行连接,得到的结果集中的总行数就是两张表中总行数的乘积,叫做笛卡尔积
	在实际中,应该要避免产生笛卡尔积的连接,特别是对于大表

SELECT  *
  FROM  matches, penalties;  --- 结果有12*8=96行
如果想专门产生笛卡尔积,可以使用交叉连接
SELECT  *
  FROM  matches 
  CROSS JOIN  penalties;

子查询

子查询解决的问题

子查询(inner  query)先执行,然后执行主查询(outer  query)

子查询返回的结果被用于外部查询中

子查询可出现在几乎所有的SELECT子句中,如:SELECT子句、FROM子句、WHERE子句、ORDER BY子句、HAVING子句等 --> 子查询可以出现在任意的部分

子查询必须放在小括号中

子查询一般放在比较操作符的右边,以增强代码可读性
不相关子查询

标量子查询(scalar subquery):返回1行1列一个值 
可以使用 = > < >= <= <> 操作符对子查询的结果进行比较
1:得到1号球队的队长的编号和姓名
SELECT playerno,NAME
  FROM players
  WHERE playerno = (
     SELECT playerno
       FROM teams
       WHERE teamno=1);

表:PLAYERS    TEAMS
字段:队长的编号(PLAYERNO) 和姓名(NAME)
条件:1号球队的队长的编号
	子查询
	SELECT PLAYERNO FROM TEAMS WHERE TEAMNO=1

	SELECT PLAYERNO,NAME FROM PLAYERS WHERE PLAYERNO = (SELECT PLAYERNO FROM TEAMS WHERE TEAMNO=1)
2:对于编号小于60的球员,得到他们加入俱乐部的年份和104号球员加入俱乐部的年份之间的差值
SELECT playerno,joined - (SELECT joined FROM players WHERE playerno=104)  year_interval
  FROM players
  WHERE playerno < 60;

需求分析
表:PLAYERS
字段:PLAYERNO,JOINED- (104号球员的加入俱乐部的年份)
条件:编号小于60
子查询:104号球员的加入俱乐部的年份

select playerno,joined-(select joined from PLAYERS WHERE playerno=104) as sub 
from PLAYERS where playerno<60;
3:得到2744号球员的生日
SELECT  (SELECT birth_date FROM players WHERE playerno=27) p27,
       (SELECT birth_date FROM players WHERE playerno=44) p44;

SELECT playerno,birth_date FROM PLAYERS WHERE playerno=27 OR playerno=44; 
能不使用子查询的尽量不要使用

注意:如果子查询返回空值,可能导致外部查询的where条件也为空,从而外部查询的结果集为空
4:查询生日小于联盟队员编号为9999的球员生日的球员的编号和姓名
SELECT playerno,NAME
  FROM players
  WHERE birth_date > (
     SELECT birth_date
       FROM players
      WHERE leagueno='9999');

查询:select
表:球员表
字段:球员的编号和姓名
条件:生日小于联盟队员编号为9999的球员生日的球员
子查询:联盟队员编号为9999的球员生日

子查询
	select birth_date from PLAYERS where leagueno=9999
主查询
	select playerno,name,birth_date from PLAYERS WHERE BIRTH)DATE > (子查询的结果)
5:找出和7号球员住在同一个城市,并且和44号球员性别相同的每个球员的编号、城市和性别
SELECT  playerno, town, sex
  FROM  players
  WHERE (town,sex) = ((SELECT town FROM players WHERE playerno=7),
                      (SELECT sex FROM players WHERE playerno=44));

注意:(,,)叫做行表达式。比较时是比较列的组合

表:PLAYERS
字段:编号、城市和性别
条件:和7号球员住在同一个城市,并且和44号球员性别相同
子查询:
	和7号球员住在同一个城市
	和44号球员性别相同
	(town,sex) = ((SELECT town FROM players WHERE playerno=7),
                      	(SELECT sex FROM players WHERE playerno=44));
		#注:这种写法更加精简
	Towm = (select town from PLAYERS where playerno=7) and sex = (select sex from PLAYERS where playerno = 44);
行子查询(row subquery):返回的结果集是 1 行 N 列
	使用行表达式进行比较,可以使用 = > < >= <= <>  in操作符

例6:查询和104号球员性别相同并且住在同一城市的球员的编号
SELECT playerno
  FROM players
  WHERE (sex,town) = (SELECT sex, town
                        FROM players
                        WHERE playerno=104)
  AND playerno <> 104; 

#注:<>  表示不等于
列子查询(column subquery):返回的结果集是 N 行 1列
	必须使用 IN、ANY 和 ALL 操作符对子查询返回的结果进行比较。其中, ANY 和 ALL 操作符不能单独使用,其前面必须加上单行比较操作符= > < >= <= <> 

	ANY	--> 和集合里所有的元素进行比较,能大于或者小于 任一一个就可以了
	ALL	--> 和集合里所有的元素进行比较,都能大于或者小于所有的	--> all 所有的
	max()
	min()
8:查询至少参加了一场比赛的球员的编号和姓名
SELECT playerno,NAME
  FROM players
  WHERE playerno IN (
    SELECT playerno
      FROM matches);
#注:in	成员关系判断

表:比赛表    球员
字段:编号和姓名
条件:至少参加了一场比赛的球员
子查询:
	比赛的球员
	select distinct playerno from MATCHES --> 集合

主查询:select playerno.name from PLAYERS where playerno in (子查询集合)
球员的名字和town,birth_date --> 查询和Wise或者Collins是老乡的球员的
	查询和Wise或者Collins是老乡的球员的名字和town,birth_date

表:球员
字段:名字和town,birth_date
条件:和Wise或者Collins是老乡
子查询:Wise或者Collins的家乡

select name,town,birth_date from PLAYERS where town in (select town from PLAYERS where name=’Wise’ or name=’Collins’) and name <> ‘Wise’ and name <> ‘Collins’;

select name,town,birth_date from PLAYERS where town in (select town from PLAYERS where name in (‘Wise’,’Collins’)) and name <> ‘Wise’ and name <> ‘Collins’;
9:查询那些最老的球员的编号、姓名和生日。最老的球员指的是出生日期小于等于 所有其它球员的球员
SELECT  playerno,NAME,birth_date
  FROM  players
  WHERE  birth_date  <=ALL  (
    SELECT  birth_date
      FROM  players WHERE birth_date IS NOT NULL
  );
10:查询除了最老的球员之外,所有其它球员的编号、姓名和生日 
SELECT  playerno,NAME,birth_date
  FROM  players
  WHERE  birth_date  >ANY  (
    SELECT  birth_date
      FROM  players
  );
SELECT PLAYERNO,AMOUNT FROM PENALTIES WHERE AMOUNT >=ALL(
SELECT DISTINCT amount FROM PENALTIES WHERE AMOUNT >40 AND AMOUNT <60)

SELECT PLAYERNO,AMOUNT FROM PENALTIES WHERE AMOUNT >=ANY(
SELECT DISTINCT amount FROM PENALTIES WHERE AMOUNT >40 AND AMOUNT <60)
	如果子查询的结果集中有null值,使用>ALLnot in操作符时,必须去掉子查询结果集中的null值,否则查询结果错误

例11:找出最大的联盟会员以及相应的球员编号
SELECT leagueno,playerno
  FROM players
  WHERE leagueno >=ALL (
    SELECT leagueno
      FROM players
     WHERE leagueno IS NOT NULL);

SELECT playerno,NAME,birth_date FROM PLAYERS WHERE
birth_date <= (SELECT MIN(birth_date) FROM PLAYERS);
表子查询(table subquery):返回的结果集是 N 行 N 列
	必须使用 INANYALL 操作符对子查询返回的结果进行比较

例13:在committee_members表中,得到任职日期和卸任日期与具有Secretary职位的一行相同的所有行
SELECT *
  FROM committee_members
  WHERE (begin_date,end_date) IN (
    SELECT begin_date,end_date
       FROM committee_members
       WHERE position=‘Secretary’);

内联视图
	--> 中间表	还是一个临时表
	用在from子句中的子查询叫做内联视图(inline view)。它用作一个数据源,外部查询再从其中检索行
	内联视图必须要定义别名,否则出错

视图:view	可以看得到的东西 --> 是一张虚表,不是真正的存在的表,是通过其他的表查询出来的一个结果集
14:得到编号小于10的男性球员的编号
	内联视图得到编号小于10的所有球员的编号和性别,并作为数据源
SELECT playerno
  FROM (SELECT playerno,sex
          FROM players
          WHERE playerno < 10) AS player10
  WHERE sex='M';

select playerno from PLAYERS where playerno < 10 and sex = ‘M’;

UNION

集合操作

	UNION [DISTINCT]
	UNION ALL
	union用于把两个或者多个select查询的结果集合并成一个
进行合并的两个查询,其SELECT列表必须在数量和对应列的数据类型上保持一致
默认会去掉两个查询结果集中的重复行
默认结果集不排序
最终结果集的列名来自于第一个查询的SELECT列表
UNION = UNION DISTINCT1:得到那些有罚款或者担任队长,或者两个条件都符合的球员的编号
SELECT playerno
   FROM teams
UNION
  SELECT playerno
    FROM penalties;
3:得到那些有罚款或者担任队长,或者住在Stratford的球员的编号
SELECT playerno
  FROM teams
UNION
SELECT playerno
  FROM penalties
UNION 
SELECT playerno
  FROM players
  WHERE town='Stratford';
如果要对合并后的整个结果集进行排序,ORDER BY子句只能出现在最后面的查询中
例:
SELECT playerno
   FROM teams
UNION
SELECT playerno
   FROM penalties
   ORDER BY playerno;
UNION  ALLUNION的区别是:前者不去掉结果集中重复的行

例:
SELECT playerno
   FROM teams
UNION ALL
  SELECT playerno
    FROM penalties;
集合运算符和NULL值
	在去重操作时,如果列值中包含NULL值,认为它们是相等的

例:最终结果集中只有1--> 即NULL值;NULL值会影响内容
SELECT playerno,leagueno
   FROM players
   WHERE playerno=7   
UNION
SELECT playerno,leagueno
   FROM players 
   WHERE playerno=7;

索引

常见问题:
	1 .什么是索引?
	2 .索引的好处和坏处
	3 .索引的类型 --> b+tree  hash
	4 .如何创建索引和删除索引?
	5 .如何知道查询是否走索引?
	6 .explain的作用
1 .什么是索引?
	全表扫描 --> 全靠运气
	索引扫描 --> 通过排好序的数据再去找内容 --> 快

	索引:index	也是一种数据,用来描述真正的数据存放的位置 --> 指针
		索引是帮助MySQL高效获取数据的数据结构
		作用:用来加快查询的速度 --> 方便查询

	mysql里的索引背后的算法是B TREE --> b+tree

	书:目录 + 内容
	目录:就是索引 --> 方便我们查找具体的知识在多少页
innodb存储引擎	文件读取	最

以上是关于09_MySQL笔记-组函数-GROUP BY-LIMIT-多表连接查询-子查询-UNION-索引-视图-存储过程的主要内容,如果未能解决你的问题,请参考以下文章

mysql_分组group by

组复制监控 | 全方位认识 MySQL 8.0 Group Replication

mysql group by 能用到索引么

MySQL笔记--- 部分 DQL 语句;条件查询;排序;分组函数;单行处理函数;group by ,having ;

mysql 可以group by 两个字段吗

PL/SQL 组函数的替代方案