全方位揭秘!大数据从0到1的完美落地之Mysql的函数

MySQL中的函数是一段可重复使用的代码,可以接受参数并返回一个值。MySQL中提供了很多内置函数,可以用于数据处理、数值计算、字符串操作、日期和时间处理等多个方面。

MySQL中的函数可以分为以下几类:

聚合函数:例如COUNT、SUM、AVG、MAX和MIN等,用于对数据进行汇总和统计。

字符串函数:例如CONCAT、SUBSTRING、LENGTH、TRIM和LOWER等,用于处理字符串数据。

数值函数:例如ABS、ROUND、CEILING、FLOOR和RAND等,用于对数值数据进行计算和处理。

日期和时间函数:例如NOW、YEAR、MONTH、DAY和DATE_FORMAT等,用于对日期和时间数据进行处理。

条件函数:例如IF、CASE和COALESCE等,用于根据特定条件返回不同的结果。

MySQL还支持自定义函数,可以使用CREATE FUNCTION语句创建自定义函数。自定义函数可以接受参数并返回一个值,与内置函数类似。使用自定义函数可以简化复杂的数据处理和计算操作,提高SQL语句的可读性和可维护性。

功能性函数

IFNULL

这个函数在之前的SQL中已经使用过了,函数有两个参数,形式为: IFNULL(field, value)

逻辑: 如果第一个参数field是NULL,则返回第二个参数value的值,否则就返回field本身的值

select ifnull(null, 10);		-- 查询结果是10
select ifnull(10, 20);			-- 查询结果是10
select ifnull(`id`, 0);			-- 如果id字段的值是NULL,就返回0,否则就返回id字段的值
IF

这个函数有点类似于Java中的三目运算符,函数有三个参数,形式为: IF(condition, value1, value2)

逻辑: 如果第一个参数condition条件成立,就返回value1的值,否则就返回value2的值。

select if(10 > 20, 10, 20);		-- 因为10 > 20不成立,因此返回20
select if(10 > 5, 10, 5);		-- 因为10 > 5成立,因此返回10
select if(`chinese` > `math`, `chinese`, `math`);	-- 如果chinese字段的值大于math字段的值,就返回chinese字段的值,否则就返回math字段的值
CASE

这个函数是MySQL中非常强大的一个函数,可以使用他来实现类似Java中的分支结构。通过模拟Java的if的实现和switch的实现,来实现一些简单的分支流程控制。case…when有两种写法:

  1. 类似Java的if

    -- 需求: 已知sc表中有一个字段名为`score`表示一个学生的成绩。通过这个成绩字段的不同范围,查询出不同的结果:
    -- < 0 或者 > 100, 等级为"错误成绩"
    -- [0, 60), 等级为"不及格"
    -- [60, 80), 等级为"良"
    -- [80, 100], 等级为"优"
    select `sid`, `score`, 
        case 
            when `score` < 0 or `score` > 100 then '错误成绩'
            when `score` < 60 then '不及格'
            when `score` < 80 then '良'
            else '优'
        end as 'level'
    from `sc`;
    
  2. 类似Java的switch

   -- 需求: 已知sc表中有一个字段名为`subject`表示成绩的科目。通过这个字段的不同的值,查询出不同的描述信息:
   -- 如果是chinese,查询出"语文"
   -- 如果是math,查询出"数学"
   -- 如果是english,查询出"英语"
   -- 其他的保持原样
   select `subject`,
       case `subject`
           when 'chinese' then '语文'
           when 'math' then '数学'
           when 'english' then '英语'
           else `subject`
       end as '翻译'
   from `sc`
行转列实现
  1. 数据准备

    snamesubjectscore
    张小三语文78
    张小三数学77
    张小三英语90
    张小三历史89
    张小三体育80
    李小四数学90
    李小四英语80
    李小四体育88
    李小四政治88
    李小四历史78
    王小五语文90
    王小五英语80
    王小五政治89
    王小五体育90
 -- 建表
 create table `sc` (
 	`sname` varchar(20),
     `subject` varchar(20),
     `score` int
 );
 -- 插入数据
 insert into `sc` values 
 	('张小三', '语文', 78),
 	('张小三', '数学', 77),
 	('张小三', '英语', 90),
 	('张小三', '历史', 89),
 	('张小三', '体育', 80),
 	('李小四', '数学', 90),
 	('李小四', '英语', 80),
 	('李小四', '体育', 88),
 	('李小四', '政治', 88),
 	('李小四', '历史', 78),
 	('王小五', '语文', 90),
 	('王小五', '英语', 80),
 	('王小五', '政治', 89),
 	('王小五', '体育', 90);
  1. 需求实现1

    姓名语文数学英语历史政治体育
    张小三78779089080
    李小四09080788888
    王小五9008008990
   -- if实现版本
   select `sname` as '姓名',
       sum(if(`subject` = '语文', `score`, 0)) as '语文',
       sum(if(`subject` = '数学', `score`, 0)) as '数学',
       sum(if(`subject` = '英语', `score`, 0)) as '英语',
       sum(if(`subject` = '历史', `score`, 0)) as '历史',
       sum(if(`subject` = '政治', `score`, 0)) as '政治',
       sum(if(`subject` = '体育', `score`, 0)) as '体育'
   from `sc` group by `sname`;
   
   -- case实现版本
   select `sname` as '姓名',
   	sum(case `subject` when '语文' then `score` else 0 end) as '语文',
   	sum(case `subject` when '数学' then `score` else 0 end) as '数学',
   	sum(case `subject` when '英语' then `score` else 0 end) as '英语',
   	sum(case `subject` when '历史' then `score` else 0 end) as '历史',
   	sum(case `subject` when '政治' then `score` else 0 end) as '政治',
   	sum(case `subject` when '体育' then `score` else 0 end) as '体育'
   from `sc` group by `sname`;
  1. 需求实现2

    姓名语文数学英语历史政治体育总成绩
    张小三78779089080414
    李小四09080788888424
    王小五9008008990349
    -- if实现版本
    select `sname` as '姓名',
        sum(if(`subject` = '语文', `score`, 0)) as '语文',
        sum(if(`subject` = '数学', `score`, 0)) as '数学',
        sum(if(`subject` = '英语', `score`, 0)) as '英语',
        sum(if(`subject` = '历史', `score`, 0)) as '历史',
        sum(if(`subject` = '政治', `score`, 0)) as '政治',
        sum(if(`subject` = '体育', `score`, 0)) as '体育',
        sum(`score`) as '总成绩'
    from `sc` group by `sname`;
    
    -- case实现版本
    select `sname` as '姓名',
    	sum(case `subject` when '语文' then `score` else 0 end) as '语文',
    	sum(case `subject` when '数学' then `score` else 0 end) as '数学',
    	sum(case `subject` when '英语' then `score` else 0 end) as '英语',
    	sum(case `subject` when '历史' then `score` else 0 end) as '历史',
    	sum(case `subject` when '政治' then `score` else 0 end) as '政治',
    	sum(case `subject` when '体育' then `score` else 0 end) as '体育',
        sum(`score`) as '总成绩'
    from `sc` group by `sname`;
    
  2. 需求实现3

    姓名语文数学英语历史政治体育总成绩
    张小三78779089080414
    李小四09080788888424
    王小五9008008990349
    总成绩1681672501671772581187
   -- if实现版本
   select `sname` as '姓名',
       sum(if(`subject` = '语文', `score`, 0)) as '语文',
       sum(if(`subject` = '数学', `score`, 0)) as '数学',
       sum(if(`subject` = '英语', `score`, 0)) as '英语',
       sum(if(`subject` = '历史', `score`, 0)) as '历史',
       sum(if(`subject` = '政治', `score`, 0)) as '政治',
       sum(if(`subject` = '体育', `score`, 0)) as '体育',
       sum(`score`) as '总成绩'
   from `sc` group by `sname`
   union
   select '总成绩' as '姓名',
       sum(if(`subject` = '语文', `score`, 0)) as '语文',
       sum(if(`subject` = '数学', `score`, 0)) as '数学',
       sum(if(`subject` = '英语', `score`, 0)) as '英语',
       sum(if(`subject` = '历史', `score`, 0)) as '历史',
       sum(if(`subject` = '政治', `score`, 0)) as '政治',
       sum(if(`subject` = '体育', `score`, 0)) as '体育',
       sum(`score`) as '总成绩'
   from `sc`;
   
   
   -- case实现版本
   select `sname` as '姓名',
   	sum(case `subject` when '语文' then `score` else 0 end) as '语文',
   	sum(case `subject` when '数学' then `score` else 0 end) as '数学',
   	sum(case `subject` when '英语' then `score` else 0 end) as '英语',
   	sum(case `subject` when '历史' then `score` else 0 end) as '历史',
   	sum(case `subject` when '政治' then `score` else 0 end) as '政治',
   	sum(case `subject` when '体育' then `score` else 0 end) as '体育',
       sum(`score`) as '总成绩'
   from `sc` group by `sname`
   union
   select '总成绩' as '姓名',
   	sum(case `subject` when '语文' then `score` else 0 end) as '语文',
   	sum(case `subject` when '数学' then `score` else 0 end) as '数学',
   	sum(case `subject` when '英语' then `score` else 0 end) as '英语',
   	sum(case `subject` when '历史' then `score` else 0 end) as '历史',
   	sum(case `subject` when '政治' then `score` else 0 end) as '政治',
   	sum(case `subject` when '体育' then `score` else 0 end) as '体育',
       sum(`score`) as '总成绩'
   from `sc`;
exists

顾名思义,就是判断数据是否存在的!exists的作用为判断一个表中的数据,是否在另外的一张表中能够查询到与之对应的数据

效率要比连接查询和子查询高!

-- 案例1: 查询有员工的部门
select distinct dept.* from dept left join emp on dept.deptno = emp.deptno where empno is not null;

select * from dept where exists(
		select 1 from emp where dept.deptno = emp.deptno
);


-- 案例2: 查询没有员工的部门
select distinct dept.* from dept left join emp on dept.deptno = emp.deptno where empno is null;

select * from dept where not exists(
		select 1 from emp where dept.deptno = emp.deptno
);

-- 案例3: 查询有部门的员工
select * from emp where exists (
		select 1 from dept where dept.deptno = emp.deptno
);

-- 案例4: 查询没有部门的员工信息
select * from emp where not exists (
		select 1 from dept where dept.deptno = emp.deptno
);

-- 案例5: 查询有下属的员工信息
select * from emp A where exists (
		select 1 from emp B where B.mgr = A.empno
);

-- 案例6: 查询有领导的员工信息
select * from emp A where exists (
		select 1 from emp B where A.mgr = B.empno
);

日期函数

函数描述
ADDTIME (date2 ,time_interval )time_interval加到date2
CURRENT_DATE ( )当前日期
CURRENT_TIME ( )当前时间
CURRENT_TIMESTAMP ( )当前时间戳
DATE (datetime )返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type )在date2中加上日期或时间
DATE_SUB (date2 , INTERVAL d_value d_type )在date2上减去一个时间
DATEDIFF (date1 ,date2 )两个日期差
NOW ()当前时间
YEAR|Month|Day(datetime )年月日

字符串函数

函数描述
CHARSET(str)返回字串字符集
CONCAT (string2 [… ])连接字串
INSTR (string ,substring )返回substring在string中出现的位置,没有返0
UCASE (string2 )转换成大写
LCASE (string2 )转换成小写
LEFT (string2 ,length )从string2中的左边起取length个字符
LENGTH (string )string长度
REPLACE (str ,search_str ,replace_str )在str中用replace_str替换search_str
STRCMP (string1 ,string2 )逐字符比较两字串大小,
SUBSTRING (str , position [,length ])从str的position开始,取length个字符
LTRIM (string2 ) RTRIM (string2 ) trim去除前端空格或后端空格

数学函数

函数描述
ABS (number2 )绝对值
BIN (decimal_number )十进制转二进制
CEILING (number2 )向上取整
CONV(number2,from_base,to_base)进制转换
FLOOR (number2 )向下取整
FORMAT (number,decimal_places )保留小数位数
HEX (DecimalNumber )转十六进制
LEAST (number , number2 […])求最小值
MOD (numerator ,denominator )求余
RAND([seed])RAND([seed])

使用示例

-- mysql常用的函数
SELECT ADDTIME('2007-12-30 21:50:50', '1:1:1') 
SELECT ADDTIME(NOW(),'1:1:1')
SELECT CURRENT_DATE()
SELECT CURRENT_TIME()
SELECT CURRENT_TIMESTAMP()
SELECT DATE(NOW())-- 获取当前时间的日期部分

SELECT DATE_ADD('2016-6-6' ,INTERVAL  -1 DAY)
SELECT DATEDIFF('2016-6-8','2016-6-10')  -- 前面的时间-后面的时间的差值
SELECT NOW();

SELECT YEAR|MONTH|DAY(DATETIME )-- 不能这样做
SELECT YEAR(NOW())-- 单独获取年
SELECT MONTH(NOW())-- 单独获取月
SELECT DAY(NOW())-- 单独获取日

-- 对字符串的操作
SELECT CHARSET('hello') -- 当前的编码格式
SELECT CONCAT('he','lo') -- 合并字符串
SELECT *,CONCAT(ename,job) FROM emp;
SELECT INSTR('hello','e')-- 当在原字符串中找不到子字符串时,会返回0
SELECT LEFT('hello',2)-- -- 不能这样做
SELECT REPLACE('hello','h','wo')
SELECT STRCMP('hello','heloo') -- 比较两个字符串   前面大返回1   后面大返回-1  相等返回0
SELECT LTRIM('       hello') -- 去除左边的空格
SELECT RTRIM('hello     ') -- 去除右边的空格
SELECT MOD(34,0); -- 返回null

自定义函数

什么是自定义函数

在上方我们已经学习了很多的函数了,但是虽然系统给我们提供的函数再怎么多,依然不能满足我们所有的需求。有时候对于一些较为复杂的逻辑,我们就需要用自定义的函数来实现。

其实自定义函数与Java中的类似,都需要指定函数的名字、函数的参数、函数的返回值和函数需要实现的逻辑功能,只是在语法上有所区别。Java的函数用Java代码来书写,MySQL的函数与后面要学习的存储过程类似,都是一组SQL集。

函数与存储过程的区别
  • 函数可以return值,存储过程不能直接return,但是有输出参数可以输出多个返回值;

  • 函数可以嵌入到sql语句中使用,而存储过程不能;

  • 函数一般用于实现较简单的有针对性的功能(如求绝对值、返回当前时间等),存储过程用于实现复杂的功能(如复杂的业务逻辑功能);

  • 函数的关键字是function, 存储过程是:procedure

自定义函数实现
  1. 定义一个函数,返回一个数字的绝对值
   -- 如果之前存在这个函数,先将其删除
   drop function if exists `my_abs`;
   
   -- 自定义分隔符,这里定义的分隔符是;; 定义好后,只有遇到;;才会结束
   delimiter ;;
   
   -- 创建函数,num为形参,returns为返回值
   create function `my_abs`(`num` int) returns int
   deterministic
   deterministic
   -- 函数开始
   begin
   	if `num` >= 0 then
   		return `num`;
   	else
   		return -`num`;
   	end if;
   -- 函数结束
   end
   ;;
   
   -- 将分隔符重新定义为;
   delimiter ;
  1. 定义一个函数,通过成绩的不同区间,返回对应的级别: 不及格、良、优、错误成绩
  -- 如果之前存在这个函数,先将其删除
  drop function if exists `get_level_info`;
  
  -- 自定义分隔符
  delimiter ;;
  
  -- 创建函数
  create function `get_level_info`(`score` int) returns varchar(20) charset utf8
  deterministic
  begin
  	if `score` < 0 or `score` > 100 then
  		return '错误成绩';
  	elseif `score` < 60 then
  		return '不及格';
  	elseif `score` < 80 then
  		return '良';
  	else
  		return '优'
  	end if;
  end
  ;;
  
  delimiter ;
  1. 定义一个函数,提取一个字符串中的中文部分
   -- 如果之前存在这个函数,先将其删除
   drop function if exists `get_name`;
   -- 自定义分隔符
   delimiter ;;
   -- 创建函数
   create function `get_name`(`name` varchar(50) charset utf8) returns varchar(50) charset utf8
   deterministic
   begin
   	declare i int default 0;
   	declare l int default char_length(`name`);
   	declare tmp varchar(50) default '';
   	
   	while i <= l do
   		if mid(`name`, i, 1) regexp '[\\u4e00-\\u9fa5]' then
   			set tmp = concat(tmp, mid(`name`, i, 1));
   		end if;
   		set i = i + 1;
   	end while;
   	
   	return tmp;
   end
   ;;
   
   delimiter ;

窗口函数

MySQL在8.0版本之后,支持了窗口函数。

窗口函数的简介

在了解窗口函数之前,我们先看这个需求应该怎么实现: 查询每一个员工的信息,及其部门的平均薪资。

-- 实现方式1: 通过子查询实现
select A.*, B.avg_sal
from emp A join (select deptno,avg(ifnull(sal,0)) avg_sal from emp group by deptno) B on A.deptno = B.deptno

-- 实现方式2: 通过子查询实现
select emp.*, (select avg(ifnull(sal,0)) from emp B where B.deptno = A.deptno)
from emp A;

通过这个查询可以看到,想要进行明细的查询和聚合统计的查询,需要进行两次查询操作,比较麻烦,而且查询效率也不高。那么,能不能简化这样的流程呢?窗口函数就应运而生了!

那么,窗口函数是什么呢?

  • 窗口函数又名开窗函数,属于分析函数的一种。
  • 是一种用于解决复杂报表统计需求的函数。
  • 窗口函数常用于计算基于组的某种值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行。简单的说窗口函数对每条详细记录开一个窗口,进行聚合统计的查询
  • 开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化。
  • 窗口函数一般不单独使用
  • 窗口函数内也可以分组和排序

image-20220117234130132

窗口函数案例

使用over()函数,为查询到的每一行数据开一个数据窗口。窗口内的数据为本次查询到的所有的数据。

一般是对窗口内的数据进行聚合统计的操作。

-- 需求: 查询每一个员工的基本信息,及所有的员工的数量
select *, count(*) over() from `emp`;

-- 需求: 查询每一个员工的基本信息,及所有员工的工资和
select *, sum(`sal`) over() from `emp`;

-- 需求: 查询每一个员工的基本信息,及所有员工的平均奖金
select *, avg(ifnull(`comm`, 0)) over() from `emp`;

-- 需求: 查询每一个员工的基本信息,及所有员工的平均工龄
select *, avg(round(datediff(now(), `hiredate`)/365, 1)) over() from `emp`;
窗口分组

通过指定的字段,对数据进行分组,此时窗口内的数据为指定分组的数据。

例如: count(*) over(partition by `deptno`), 此时将数据通过deptno字段进行了分组,窗口内的数据是与本行的deptno相同的数据。

-- 需求: 查询每一个员工的基本信息,及其部门的人数
select *, count(*) over(partition by `deptno`) from `emp`;

-- 需求: 查询每一个员工的基本信息,及其部门的最高工资
select *, max(`sal`) over(partition by `deptno`) from `emp`;

-- 需求: 查询每一个员工的基本信息,及其部门的平均薪资
select *, avg(ifnull(`sal`, 0)) over(partition by `deptno`) from `emp`;

-- 需求: 查询每一个员工的基本信息,及部门的平均工龄
select *, avg(round(datediff(now(), `hiredate`)/365, 1)) over(partition by `deptno`) from `emp`;

-- 需求: 查询每一个员工的基本信息,及当前岗位的人数
select *, count(*) over(partition by `job`) from `emp`;
窗口数据排序

使用order by子句,可以实现窗口内的数据按照指定的条件进行升序或者降序的排列。此时需要注意的是: 窗口内的数据会逐行递增

select *, sum(`sal`) over(partition by `deptno` order by `sal` desc) from `emp`;
排名函数

有了窗口函数,就可以对数据进行排名次了。常见的排名函数有三种: row_number()、rank()、dense_rank();

  • row_number: 对每一行的数据进行编号,不会出现相同的名次
  • rank: 常见的排名函数,会出现并列的名次,同时会出现名次跳跃的情况
  • dense_rank: 常见的排名函数,会出现并列的名次,不会出现名次跳跃的情况
scorerow_numberrankdense_rank
100111
99222
99322
98443
98543
97664
-- 查询每一个员工的基本信息,及员工在自己部门内的工资排名
select *, rank() over(partition by `deptno` order by `sal` desc) from `emp`;

-- 查询每一个员工的基本信息,及员工在所有人中的工资排名
select *, dense_rank() over(order by `sal` desc) from `emp`;

-- 查询每一个员工的基本信息,及员工在同一个工种内的工资排名
select *, rank() over(partition by `job` order by `sal` desc) from `emp`;

-- 查询每一个员工的基本信息,及员工在所有人中的工龄排名
select *, rank() over(order by `hiredate`) from `emp`;

-- 查询每一个员工的基本信息,及员工在当前部门中的工龄排名
select *, rank() over(partition by `deptno` order by `hiredate`) from `emp`;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值