编写复杂的sql语句要掌握的知识

1 基础sql

    INSERT INTO 表名 VALUES(第一列对应数据, '第二列对应数据');

image

    删除符合指定条件的所有数据

    Delete from 表名 where条件

image

    使用update关键字

    修改表中某一列的所有值为指定值

    Update 表名 set列名=新值;

image

    修改符合指定条件的所有数据为指定值

    Update 表名 set列名1=新值,列名2=新值 where 用作条件的列名=用作条件的值;

image

    Select * from 表名 where 条件;

    查询并按照给定顺序展示指定列:

    将查询条件中的*改为 列名1,列名2,列名3 即可 如果选择了多列,那么列与列之间用逗号隔开,并且列名都可以通过空格“ ”或则“as”起别名

image

2 常用的查询的复杂字段

2.1 where子句的条件

https://blog.csdn.net/axl19530209/article/details/43950953

类别 运算符 说明
比较运算符 =、>、<、>=、<=、<>、!= 比较两个表达式
逻辑运算符 AND、OR、NOT 组合两个表达式的运算结果或取反
范围运算符 BETWEEN、NOT BETWEEN 搜索值是否在范围内
列表运算符 IN、NOT IN 查询值是否属于列表值之一
字符匹配符 LIKE、NOT LIKE 实现模糊查询,字符串是否匹配
判空运算符 IS NULL、IS NOT NULL 查询值是否为NULL
  1. 字符串利用=判断是否相等 而且字符串可以用单引号或双引号引起来

    利用!=和<>可以表示不等于

    利用>=和<=可以表示大于等于和小于等于

  1. 日期可以直接进行比较,2021-5-1小于2021-6-1

image

  1. between and 表述的是闭区间

  2. 利用 and or时 不能直接在两侧接值 都得有属性=值 构成完整语句

  3. and优先级比or大,在混用操作符时想执行and,后面执行or

select * from 表名 where 条件1 and 条件2 OR 条件3 and 条件4;
上述sql语句会查找同时满足条件1和条件2的数据以及同时满足条件3和条件4的语句,等价于
select * from 表名 where 条件A OR 条件B;

  1. or表示对两个结果集取合集,会去重,当条件里有or时,经常会改写成union

  2. 只有查询出来的数据 不确定具体数量 才用in

    明确知道查询的数据时 不要用in 而是用or 这样能提升数据库性能 因为in可以理解为多重循环 所以很占资源

image

  1. 使用where A in B时,如果B是一张n行m列的表,那么A可以由m个属性构成的单行矩阵来对应

    例如这个实例:

image

  1. 如果字段是char和varchar型,用等号判断会比较好;如果是int型,用ISNULL判断比较好

2.2 去重复distinct 列名

使用distinct 将指定的列中不相同的数据展示出来

如下图,对员工信息表使用,每个员工都有自己对应的部门编号,最后去重复后就能获得一个单列多行矩阵,展示数据库中该列不重复的数据

image

image

distinct还能在聚合函数中使用

select count(字段A)/count(distinct 字段A) from 表名

2.3 模糊查询 like

使用关键字 like

配合表示存在0个或多个其他数据的字符 %

配合表示占位符的下划线 _ 一个下划线对应一个任意字符

表示字符串可以用单引号 也可以用双引号

Select * from 表名where 列名 like 条件;

image

image

2.4 排序order by 列名 asc/desc

2.4.1 简单 根据单列排序

Select * from 表where 条件 order by 列名 desc/asc

这里desc表示降序 asc表示升序 不指定时默认为asc

如果不指定条件 即没有where 条件 即不筛选数据 那么就是对全表的数据进行排序

image

筛选数据后,在筛选的结果上进行排序:

image

2.4.2 复杂 根据多列排序

如果要求整个表先按照列1升序,再按照列2降序,那么可以用order by 列1 排序方式,列2 排序方式......

多列之间逗号隔开,每列后接排序方式,列与列之间存在先后顺序

select * from 表名 order by 列1 asc,列2 desc;

实例:

按照gpa、年龄降序排序输出

image

结果,可以看出整表先按照gpa降序,而age是乱序,这是因为order by 先保证gpa降序,当数据的gpa相同时,再根据age降序排列

image

2.5 聚合函数 用来对列进行计算

2.5.1 基本使用

image

实例1:直接对整个表计算 这里*表示整个表 实际上只要一行中某一格不为null 那么就会被计入

在计算函数后可以接字符串用来修改展示格式

image

实例2:对指定列计算 这里height就是指定的列

image

2.5.2 分组使用

如果一个查询使用了分组 group by,那么聚合函数会在分组之后,对每个组的指定列分别进行计算

2.6 分组 group by 列名

2.6.1 简单 根据单例分组

select * from 表名 group by 列名;
会根据指定的列名出现的不同的值进行分组
实测SQL会先执行筛选(where) 再执行分组( group by) 并且写代码时要先写筛选再写分组 否则会报错

image

实例1:根据指定的列的值对全表数据进行分组 然后列出每个组的第一条数据

image

但是仅仅是分组然后给出第一条没有意义,我需要的是分组后根据需求给出数据

所以需要进一步结合聚合函数设置输出格式:

根据指定的列(sex)对整个表进行分组 然后输出n行2列的矩阵

一列为分组后的sex值

另一列为对应sex值的行数数量

Select sex,count(sex) from 表名 group by sex;

image

结果:

image

2.6.2 复杂 根据多个列分组

https://blog.csdn.net/weixin_38750084/article/details/82780519

select * from 表名 group by 列名1,列名2

这样可以根据多个字段进行分组,这些列名存在顺序关系,假如一整张表按照列名1分为A\B两组,然后再对A\B两组分别按照列名2进行细分,得到A-1、A-2、A-3等组,以及B-1、B-2等组。

实例:

假设现在整表查询如图

image

使用group by university,gender

mysql首先会将整个表按照university分组,同组的数据调整顺序放到一块,如果此时没有gender,则直接展示每组第一条数据。

image

由于这里还有gender,因此mysql在根据university分组并调整顺序后,在此基础上进一步对每组按照gender分组。

image

2.7 连表查询

image

2.7.1 子查询

具备主外键是连表查询的基础,但是通常不允许使用主外键,只允许从业务逻辑进行连表,所以常用的是子查询而不是内、外连接

一个查询当做另一个查询的条件、列、表 那么这个查询叫子查询

把一个查询当做另一个查询的条件,即写在where之后

把一个查询当做临时列来用,即写在select和from之间

把一个查询当做另一个查询的临时表,即写在from和where之间

写法:一般都将子查询放在()里

2.7.2 内连接

2.7.2.1 简单的两表

两种写法

select * from 表1 inner join 表2 on 表1=表2的关联条件(主外键那列)

select * from 表1,表2 where 表1=表2的关联条件(即主外键)

2.7.2.2 复杂的多表

举例,三表联查,使用内连接

SELECT 
  *
FROM 表1 a,
     表2 b,
     表3 c
WHERE
    a.列1 = b.列1
    and 
    a.列2 = c.列1

2.7.3 外连接

2.7.3.1 简单的两表

分左右连接

左连接:将以左边的表作为主表,右边的表没有的数据会自动设置为null

select * from 表1 left join 表2 on 表1=表2的关联条件

在这里表1是左表 即主表

右连接:将以右边的表作为主表,左表没有的数据将自动填充null

select * from 表1right join 表2 on 表1=表2的条件(即主外键对应的列)

在这里表2是右表 即主表

2.7.3.2 复杂的多表

举例,三表联查,使用左连接

SELECT
*
FROM 表1 AS a
LEFT JOIN 表2 AS b
ON a.列1=b.列1
LEFT JOIN 表3 AS c
ON a.列2=c.列1;

2.7.4 养成指定属性所属表的习惯

不依靠主外键约束的两表连接,要养成对属性都指定原归属表的习惯

image

如上代码,两表依靠业务逻辑,使用where进行的软连接,同时两表存在同名列——id, 虽然从上面的图可以看出能正常的拼合,且同名的列被自动重命名(靠右的id列变为id1),但是涉及到分组、聚合时,如果使用了同名的列,就必须进一步指明列的原归属表,否则就会报错

image

2.7.5 自连接

其实就是将表自己与自己连接,然后使用别名来区分两个“自己”,最后按照两表连接进行之后的数据操作,注意此时对列的操作都要用别名进行指定,因为自己和自己拼合,不但属于内连接(等值连接),列还全都同名。

自连接目前用的最多的就是子类拥有父类的id,需要查询出父类id对应的父类名字

图中的表就它本身,自己的id和自己的父类id关联,用了自连接和子查询

image

2.8 分页limit

mySQL独有的语句 ,给最终的查询结果进行分页展示

两种写法

limit N:从第一条开始查,查N条 此时N代表显示结果最多N条

limit N,M:从第N条开始查,查M条 此时N代表的是索引 N=0代表第一条 M代表的是显示结果最多M条

2.9 集合运算 Union

2.9.1 union

Union 放在两个select语句中间起到连接作用,Union 在两个select存在一样的数据时,只取一个,也就是自带去重

语法:

Select * from 表1 ;结果1
Union
Select * from 表2 ; 结果2

要求结果1和结果2得到的查询列数要一致

实例

image

2.9.2 union all

Union all ,会将两select的数据全部取到,组成一个表,但是不去重

image

2.10 条件 having

https://www.runoob.com/sql/sql-having.html

在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数一起使用。

HAVING 子句可以让我们筛选分组(group by)后的各组数据。

where 和having都是筛选条件,但是有区别的:

1.where在group by前, having在group by 之后

2.聚合函数(avg、sum、max、min、count),不能作为条件放在where之后,但可以放在having之后

2.11 判空 列名 is null

如果要筛选某列为空的数据

select * from 表名 where 列名is null;

反之,要获取某列不为空

select * from 表名 where 列名is null null;

2.12 范围内取值 列名 in ("字段1","字段2"......)

如果需要查询某个字段等于A或者等于B或者等于C,那么就要用in

select * from 表名 where 字段 IN ("A","B","C");

反之,要求某个字段不等于A和B和C,那么就可以用not in

select * from 表名 where 字段 not IN ("A","B","C");

2.13 sql的精度

sql在对整数做除法运算时,会自动将结果转为浮点数,并且默认最多保留4位小数

select 1/1 as a; //1
select 1/2 as a; //0.5
select 1/3 as a; //0.3333
select 1/4 as a; //0.25

但是如果使用了聚合函数count,那么就会直接保留4位小数

没有表的基础上 直接在查询里做运算 此时count(1)=1
select count(1)/count(1) as b; //1.0000
select count(1)/1 as b; //1.0000
select count(1)/2 as b; //0.5000
select count(1)/3 as b; //0.3333
select count(1)/4 as b; //0.2500

特别的,使用聚合函数后,可以乘以浮点数提升精度,这是整数做除法运算时无法做到的

select count(1)/4*1.0 as b; //0.25000
select count(1)/4*1.00 as b; //0.250000

2.14 改变精度的三种方式

https://blog.csdn.net/weixin_43792309/article/details/107511016

2.14.1 乘一个浮点数

如果使用了多个聚合函数并且使用了除法,比如下面的sql,此时结果只会保留4位小数

select count(字段A)/count(distinct 字段A) from 表名

可以将其中一个数通过乘一个浮点数提升精度

select count(字段A)*1.0/count(distinct 字段A) from 表名

特别的,乘以1.0和乘以1.00是不一样的,后者会导致小数更多一位,也就是更精确

2.14.2 cast(数据 as 新的数据类型) 配合decimal(p,d)

http://www.360doc.com/content/17/0328/15/40081561_640836375.shtml

https://www.cnblogs.com/owenma/p/7097602.html

使用cast()函数可以改变数据类型

select cast(count(字段A) as double)/count(distinct 字段A) from 表名

如果使用cast(),一般会配合数据类型decimal(p,d)

select cast(count(字段A) as decimal(10,4))/count(distinct 字段A) from 表名

P是表示有效数字数的精度。 P范围为1〜65
D是表示小数点后的位数。 D的范围是0~30。MySQL要求D小于或等于(<=)P

举个例子,decimal(10,4)代表一个数整数部分位数+小数部分位数<=10,并且小数部分位数指定为4位

2.14.3 round(数据,要保留几位小数)

https://www.w3school.com.cn/sql/sql_func_round.asp

还是同样的例子,可以将计算结果保留指定位数的小数

select round((count(字段A)/count(distinct 字段A)),4) from 表名

又或者只是对显示结果进行小数点设置

select round(字段B,4) from 表名

2.15 if条件函数

https://blog.csdn.net/weixin_41758552/article/details/105437649

if条件函数会对给定的表每一行数据进行判断,符合条件则执行操作1,否则执行操作2

select if(条件,操作1,操作2) as 自定义列名 from 表名

实例

现在有一张表如图,有7条数据

image

现在使用if函数,可以做到对每条数据都执行if判断,再根据结果执行不同的操作

SELECT IF (age >= 25,"25岁及以上","25岁以下") AS 自定义列名FROM user_profile

7条数据被逐一判断,如果age>=25,就输出“25岁及以上”,反之输出“25岁以下”,而这些输出的信息会被放在单独的一列,列名自定义,如果不指定列名,那么就会把一整个if函数作为列名

image

2.16 case函数

https://blog.csdn.net/cpc784221489/article/details/90300424

2.16.1 简单case函数

有点像java的switch-case,按从上到下的书写顺序将col_name的值与每个WHEN子句的value进行比较,如果相等则返回对应的result,如果都不想等则返回ELSE子句的result,如果没有ELSE子句则返回null

形式

CASE  <col_name>
   WHEN <value1> THEN <result1>
   WHEN <value2> THEN <result2>
   ...
   ELSE <result>
END 

例如

select  day
    when  1  then  "星期一"
    when  2  then  "星期二"
    when  3  then  "星期三"
    when  4  then  "星期四"
    when  5  then  "星期五"
    when  6  then  "星期六"
    else  "星期天"
end 

2.16.2 case搜索函数

对给定表的每一个数据都进行判断,例如满足条件1,就返回结果1,一旦满足条件,就不再进行后续判断,如果都不满足则返回ELSE子句的结果,如果没有ELSE子句则返回null

形式

CASE
    WHEN <条件1> THEN <结果1>
    WHEN <条件2> THEN <结果2>
    ...
    ELSE <结果>
END 

例如

case 
    when  score<60  then"不及格"
    when  score <=70  then "及格"
    when  score<=80   then "中等"
    when  score<=90   then "优良"
    else   "优秀"
end  

2.17 日期函数

https://www.cnblogs.com/feiquan/p/8645885.html

日期函数很多,常用的是day() month() year()这三个函数,可以对data类型的字段分别取出日、月、年,再配合where进行判断

2.18 文本函数 字符串截取substring_index

https://zhuanlan.zhihu.com/p/109778760

substring_index(str,delim,count)
str:要处理的字符串
delim:分隔符
count:计数

示例:

如 str=www.wiki.com

则 substring_index(str,'.',1) 处理的结果是:www

substring_index(str,'.',2) 得到的结果是:www.wiki

也就是说,如果count是正数 ,那么就是从左往右数 ,第N个分隔符的左边 的全部内容,

相反,如果是负数 ,那么就是从右边开始数 ,第N个分隔符右边 的所有内容。

如:

substring_index(str,'.',-2) 得到的结果为:http://wikibt.com

如果要中间的的 wiki 怎么办?

很简单的,需要从两个方向截取,也就是分两步:

先截取从右数第二个分隔符的右边的全部内容,再截取从左数的第一个分隔符的左边的全部内容:

substring_index(substring_index(str,'.',-2),'.',1);

2.19 窗口函数

https://zhuanlan.zhihu.com/p/92654574

2.19.1 应用场景说明

以往做排名,都是给定人员A\B\C,然后进行排序,但是如果给定的是A\B\C部门,然后要求出各个部门前N名人员,那么除了各个部门分别处理数据,最后再用union合并数据外,还可以考虑使用mysql的高级函数功能——窗口函数

2.19.2 基本语法说明

窗口函数的基本语法如下:

<窗口函数> over (partition by <用于分组的列名> order by <用于排序的列名> asc/desc)

asc/desc不指定时默认为asc升序

<窗口函数>的位置,可以放以下两种函数:

1) 专用窗口函数,包括后面要讲到的rank, dense_rank, row_number等专用窗口函数。

2) 聚合函数,如sum,avg, count, max, min等

因为窗口函数是对where或者group by子句处理后的结果进行操作,所以窗口函数原则上只能写在select子句中

2.19.3 窗口函数和group by的区别

窗口函数具备了我们之前学过的group by子句分组的功能和order by子句排序的功能。那么,为什么还要用窗口函数呢?
这是因为,group by分组汇总后改变了表的行数,一行只有一个类别。而partiition by和rank函数不会减少原表中的行数 。例如下面统计每个班级的人数。

image

2.19.4 专业窗口函数的区别

有三种专业窗口函数:rank(), dense_rank(), row_number()

对一张表,执行查询如下

select *,
   rank() over (order by 成绩 desc) as ranking,
   dense_rank() over (order by 成绩 desc) as dese_rank,
   row_number() over (order by 成绩 desc) as row_num
from 班级表

//在上述的这三个专用窗口函数中,函数后面的括号不需要任何参数,保持()空着就可以。

结果如图

image

对比:

    - rank函数:这个例子中是5位,5位,5位,8位,也就是如果有并列名次的行,会占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1 ,4。
    

    - dense_rank函数:这个例子中是5位,5位,5位,6位,也就是如果有并列名次的行,不占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1 ,2。
    

    - row_number函数:这个例子中是5位,6位,7位,8位,也就是不考虑并列名次的情况。比如前3名是并列的名次,排名是正常的1,2,3 ,4。

更简单的一图流

image

2.19.5 聚合函数作为窗口函数

聚和窗口函数和上面提到的专用窗口函数用法完全相同,只需要把聚合函数写在窗口函数的位置即可,但是函数后面括号里面不能为空,需要指定聚合的列名。

聚合函数作为窗口函数,可以在每一行的数据里直观的看到,截止到本行数据,统计数据是多少(最大值、最小值等)。同时可以看出每一行数据,对整体统计数据的影响。

如果想要知道所有人成绩的总和、平均等聚合结果,看最后一行即可。

具体使用方式见上方的帖子。

3 sql语句执行顺序

聚合函数执行顺序在group by之后

case()函数执行顺序在group by之前

image

4 DDL等名词说明(SQL组成)

名词解释

  1. DDL(Data Definition Language): 数据定义语言

    - 用来定义数据库对象:数据库,表,列等。 关键字:create,drop, alter等。

  1. DML(Data Manipulation Language): 数据操作语言

    - 用来对数据库中的表进行增删改操作。 关键字:insert,delete,update等。

  1. DQL(Data Query Language): 数据查询语言

    - 用来查询数据库中表的记录(数据)。 关键字:select, where等

  1. DCL(Data Control Language): 数据控制语言

    - 用来定义数据库的访问控制权限和安全级别,及创建用户。关键字: grant, revoke等

SQL主要分成四部分

(1)数据定义。(SQL DDL)用于定义SQL模式、基本表、视图和索引的创建和撤消操作。

(2)数据操纵。(SQL DML)数据操纵分成数据查询和数据更新两类。数据更新又分成插入、删除、和修改三种操作。

(3)数据控制。(DCL)包括对基本表和视图的授权,完整性规则的描述,事务控制等内容。

(4)嵌入式SQL的使用规定。(TCL)涉及到SQL语句嵌入在宿主语言程序中使用的规则。

5 题目

第二高的薪水

    题目描述

image

    答案

select ifNull((select distinct Salary from Employee order by salary desc limit 1,1 ),null) as SecondHighestSalary;

    说明

    1. select ifNull((查询),查不到就要返回什么信息) as 自定义表名通常是题目名
    

    2. select ditinct 去重依据字段 from 表 加了ditinct字段能去重

    3. order by 排序依据的字段 asc/desc

    4. limit 1,1 查询第二高的数据,同理,查询第三高就用limit 2,2

查找重复的数据

    题目描述:

image

    答案:

解法1:having +聚合函数 > select Email from Person group by Email having count(Email) > 1;

解法2:自链接 > select DISTINCT p1.Email from Person p1, Person p2 where p1.Email = p2.Email and p1.Id != p2.Id;

解法3:子表查询 > select Email from (select count(1) as t, Email from Person group by Email) r where r.t > 1;

    说明:

    三种解法

    1. having+聚合函数 ,在全表查询的基础上使用having进行过滤,相比where可以使用聚合函数count 再配合比较符号>1即可查询重复数据

    2. 自连接,使用where将需要查询重复数据的字段连接,同时要求主键不相同,就能得到一张记录多条相同数据但主键id不同的表,最后使用去重DISTINCT

    3. 子查询,首先将表按照Email分组,然后使用聚合函数count统计指定列1记录不为null的条数,并给这个字段一个别名t,这样就可以知道每一个邮箱有多少条,再然后把这个表作为子查询,给别名r,再从这个r表使用where判断t字段>1的email。

        实话说有点绕,其实核心就是聚合函数+where,由于where不能用聚合函数,只能先在子查询中用聚合函数再用where判断,还是解法一简便

查找不在表中的数据

    题目描述

image

    答案

select a.Name as Customers from Customers as a left join Orders as b on a.Id=b.CustomerId where b.CustomerId is null;

    说明

    其实就是多表联查,上面总结了都有哪些连表方式2.7 连表查询,题目的意思就是在查找在顾客表但是不在订单表的数据

    遇到要查找“不在表里的数据 ,也就是在表A里的数据,但是不在表B里的数据。”可以使用下图黑框里的sql语句。

image

having的一些概念

image

    解答一

        A F:having子句即可包含聚合函数作用的字段也可包括普通的标量字段,实施上所有的where子句都可以用having代替

        B、C、E:where子句是过滤行;having子句是过滤分组。where在数据分组之前使用,having在数据分组之后使用,可以同时使用。

        D:group by 子句是限定分组条件的,having是过滤分组的

    解答二

        - where子句 = 指定行所对应的条件

        - having子句 = 指定组所对应的条件

        - D中是Group by才用来分组的,group by的作用是限定分组条件,而having则是对group by中分出来的组进行条件筛选。

        - 所以用having就一定要和group by连用,且是先group by XXX 再having XXX,用group by不一有having(它只是一个筛选条件用的)

        - having 是对 group by后的数据进行筛选过滤,必须要有group by才能用having

        - having只用来在group by之后,having不可单独用,必须和group by用

转存数据

    题目描述

        某打车公司要将驾驶里程(drivedistanced)超过5000里的司机信息转存到一张称为seniordrivers 的表中,他们的详细情况被记录在表drivers 中,正确的sql语句为()

    答案

select * into seniordrivers from drivers where drivedistanced >=5000

    说明

        INSERT INTO 语句用于向表格中插入新的行

INSERT INTO table_name VALUES (值1, 值2,....)
指定所要插入数据的列:
INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)

        SELECT INTO 语句从一个表中选取数据,然后把数据插入另一个表中 。常用于创建表的备份复件或者用于对记录进行存档。

把所有的列插入新表 
SELECT *
INTO new_table_name [IN externaldatabase] 
FROM old_tablename

只把希望的列插入新表 
SELECT column_name(s)
INTO new_table_name [IN externaldatabase] 
FROM old_tablename

        

查询语句的统计函数辨析

    题目描述

image

    答案

        A\B\C\E是一组,结果一样

        D\F是一组,结果一样

        由于这是一个选择题,给出的答案中只有D,F选项符合题意,所以答案是D\F

    说明

        1. 所有统计函数函数都是在where筛选之后,针对筛选好的表进行的

        2. sum(指定字段)会获得筛选后整个表的score字段的和

        3. count(*)会获得筛选后整个表的行数

        4. count(1)可以理解为count(数字),也是获得整个表的行数

        5. count(指定字段)会获得整个表中这个字段不为null的行数

        6. avg(score)等价于sum(score)/count(score)

        7. 每条sql语句意思如下:

            A :分数之和/所有学生,分数之和不包括分数为null的记录,但会把分数为null的学生也算入分母,徒增分母的值

            B :与A一样,因为id主键非空,count(id)所得分母基数是所有学生。

            C : 与B一样,sno也是非空属性。

            D :由于score字段的值可能是空,空值在统计时忽略,所以count(score)和sum(score)统计的只是score不为空的学生,计算得到的平均分也只是有成绩的学生的平均分,无法计算所有学生的平均分。

            E: 与A一样,因为count(1)与count(*)一样。

            F:和D一样,avg(score)等价于sum(score)/count(score)

            总的来说A,B,C,E返回sum(score)除以行数;D,F返回sum(score)除以score不为null的行数

        8. 对于count,有三种情况,对应四种表现形式

            此处设定count(a),其中a为变量,可以为各种值,下面根据a的不同值,得出不同的count(a)的结果

            1)当a = null时,count(a)的值为0;count()

            2)当a != null 且不是表的列名的时候,count(a)为该表的行数;count(*)或者count(数字)

            3)当a是表的列名时,count(a)为该表中a列的值不等于null的行的总数,它和2)中的差值就是该表中a列值为null的行数count(指定列)

        9. count()和count(1)可以看成count()与count(不为列名的数字),从结果上看是一样的,不同的是两者的性能

        10. 性能方面:count(数字)和count(*)均>count(具体字段)

            因为在sql的引擎中,分完组之后,count(*)与count(数字)其实结果就已经出来了,但是如果写了count(具体字段),引擎还会在遍历一遍分完组的数据,统计具体字段不为空(null)的条数,然后返回出来。

        11. count(数字)和count(*)分情况:

            如果在开发中确实需要用到count()聚合,那么优先考虑count(*),因为mysql数据库本身对于count()做了特别的优化处理。

            - 有主键或联合主键的情况下,count(*)略比count(1)快一些。

            - 没有主键的情况下count(1)比count()快一些。

            - 如果表只有一个字段,则count(*)是最快的。

        12. 使用count()聚合函数后,最好不要跟where age = 1;这样的条件,会导致不走索引,降低查询效率,除非该字段已经建立了索引。

        13. 使用count()聚合函数后,若有where条件,且where条件的字段未建立索引,则查询不会走索引,直接扫描了全表。

        14. count(字段),字段是非主键字段,这样的使用方式最好不要出现,因为它不会走索引。

分组排序

    题目

image

    答案

select university,AVG(question_cnt) from user_profile GROUP by university ORDER BY AVG(question_cnt) asc;

    说明

    1. 根据sql语句执行顺序,整个表首先被group by根据指定列分成多个组

    2. 然后每个组都会被avg()计算出属于这个组的数据,也就是说avg()直接面向组执行多次,而不是面向整张表只执行一次

    3. 之前的理解是order by会根据指定数据对整个表的数据进行排序,在这里order by会对每个组依据指定的数据进行排序,其实也可以理解成group by+avg()之后,整个表只显示每组第一条数据,然后还有一列隐藏列记录每组的avg值,然后order by对这张表的数据根据这个隐藏列进行排序

    4. order by排好序后,整张表再显示university字段和隐藏列的数据,也可以理解为显示university,然后再对各个组进行一次avg计算,再显示出来

结果合并不去重

    题目

image

    答案

SELECT device_id,gender,age,gpa FROM user_profile WHERE university="山东大学" 
union ALL
SELECT device_id,gender,age,gpa FROM user_profile WHERE gender="male"

    说明

    1. 限定条件:学校为山东大学或者性别为男性的用户:university='山东大学', gender='male'

    2. 分别查看&结果不去重:所以直接使用两个条件的or是不行的,直接用union也不行,要用union all,分别去查满足条件1的和满足条件2的,然后合在一起不去重

    3. or是取两个结果集的合集,union也是取两个结果集的合集,这两者都会导致去重,如果不希望去重,就要用union all

计算符合条件的统计结果

    题目

image

    答案

//解法1 if函数
select age_cut, count(*)
from (
    select 
        if(age>=25, "25岁及以上", "25岁以下") as age_cut
    from user_profile
) as up
group by age_cut

//解法2 union
SELECT '25岁以下' age_cut,COUNT(device_id) Number
FROM user_profile
WHERE age < 25 OR age IS NULL
UNION
SELECT '25岁以及上' age_cut,COUNT(device_id) Number
FROM user_profile
WHERE age >= 25

//解法3 case函数
SELECT 
  CASE 
  WHEN age < 25 OR age IS NULL THEN '25岁以下' 
  WHEN age >= 25 THEN '25岁及以上'
  END as age_cut,COUNT(*) as number
FROM user_profile
GROUP BY age_cut

    解法一说明

    1. 先看子查询,用if函数,对整个表的数据逐一判断,获得一个自定义表

        子查询结果如图

image

    2. 然后就是对子查询获得的表分组,并使用聚合函数count

    解法二说明

    1. 解法二就是分别查询满足条件的数据,然后用union拼接

    解法三说明

    1. 使用了case搜索函数,就像用if函数一样,会得到一列自定义列结果

image

    2. case/if这种函数执行顺序在group by之前

统计人数,需要的信息放在文本内

    题目

image

    答案

select
    substring_index(profile, ',', -1) as gender,
    count(device_id) as number
from user_submit
group by gender

    说明

    1. 题目需要根据性别统计人数,但是目前表上没有性别字段,只有一个profile,但是可以看到这个字段很规律,所以可以通过substring_index函数,对每一行数据的这个字段进行处理,得到性别数据,放在自定义字段gender中,然后再用这个gender字段分组,最后使用count函数进行统计

专业窗口函数使用

    题目

image

    答案

//解法一 窗口函数
select device_id, university, gpa
from (
    select *,
    row_number() over (partition by university order by gpa) as rn
    from user_profile
) as univ_min
where univ_min.rn=1

//解法二 将表a的device_id,university,gpa和表b的university,min(gpa)连接起来找
SELECT a.device_id,a.university,a.gpa FROM user_profile a
JOIN (SELECT university,min(gpa) gpa FROM user_profile GROUP BY university) b
on a.university=b.university and a.gpa=b.gpa
ORDER BY university;

    说明

    1. 这是典型的多组中求每组前N或者后N名,需要使用分组和排序,可以考虑连表实现,也可以考虑使用窗口函数

    2. 使用窗口函数时,首先在user_profile表使用row_number() over(partition by 分组依据 order by 排序依据)得到分组并排序好的表univ_min 此时各个数据都会在列rn中记录自己在本组的序号

    3. 然后查询表univ_min中rn=1的数据,即可得到题目需求的每组中最值人员

posted @ 2021-11-16 17:36  夏·舍  阅读(1022)  评论(0编辑  收藏  举报