MySQL常用函数

1. length 和char_length() 的区别?

# mysql 中 length() 函数: 一个汉字等于两个或者三个字符
# length函数用来计算普通字符的长度,他会把一个中文字符的长度按照设置的对应的字符集计算为2或3。
# 1、utf8字符集编码下,一个汉字是算三个字符,一个数字或字母算一个字符
# 2、其他编码下,一个汉字算两个字符,一个数字或字母算一个字符
select uid   , nick_name  , length(nick_name) ,char_length(nick_name) from user_info   ;



#  char_length()
# char_length函数可以计算unicode字符,包括中文等字符集的长度
# 1. 长度的单位为字符,一个多字节字符(例如:汉字)算作一个单字符
# 2、不管汉字还是数字或者是字母都算是一个字符;
# 3、任何编码下,多字节字符都算是一个字符;

# demo:

# +----+-----------------+-----------------+----------------------+
# |uid |nick_name        |length(nick_name)|char_length(nick_name)|
# +----+-----------------+-----------------+----------------------+
# |1001|牛客1              |7                |3                     |
# |1002|牛客2号             |10               |4                     |
# |1003|牛客3号♂            |13               |5                     |
# |1004|牛客4号             |10               |4                     |
# |1005|牛客5678901234号    |19               |13                    |
# |1006|牛客67890123456789号|23               |17                    |
# +----+-----------------+-----------------+----------------------+

1. 如何判断某个字段是正数,零还是 负数? 用sign ()

sign( )函数:判断数值的正负性,如果数值是正数,返回值是1,如果该数值是负数,返回值是-1,如果该数值是 0,返回值也是0。

  格式:

    select sign(数值) from 表名

1. 字符串获取 靠前,最后的几个字符

# RIGHT(s,n)	返回字符串 s 的后 n 个字符
# LEFT(s,n)	返回字符串 s 的前 n 个字符
# MID(s,n,len)	从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len)
# LCASE(s)	将字符串 s 的所有字母变成小写字母

#
 select right('abcde',2) ;
# de
 select left ('abcde',2) ;
# ab
select id ,name    from girl where id in (1,2,3,4)  ;

# 判断一个字段中 是否 含有字符串helloworld
# 注:MySQL中的模糊查询 like 和oracle中的instr()函数有同样的查询效果; 如下所示:
#
# select * from tableName a where name like '%helloworld%';
#
# select * from tableName a where instr(name,'helloworld')>0; --这两条语句的效果是一样的


# +--+----------------+
# |id|name            |
# +--+----------------+
# |1 |慕容吹雪-慕容博-慕容吹雪   |
# |2 |慕容吹雪-慕容吹雪-慕容吹雪2 |
# |3 |慕容吹雪3-慕容吹雪-慕容吹雪3|
# |4 |慕容吹雪4           |
# +--+----------------+

#
#  截取 第一个 instr(str,sonStr) 获取 sonStr  在 str 中出现的下标,下标是从 1开始的
select id ,name  ,substr(name,1,instr(name,'-')-1)  from girl where id in (1,2,3,4)  ;

# +--+----------------+--------------------------------+
# |id|name            |substr(name,1,instr(name,'-')-1)|
# +--+----------------+--------------------------------+
# |1 |慕容吹雪-慕容博-慕容吹雪   |慕容吹雪                            |
# |2 |慕容吹雪-慕容吹雪-慕容吹雪2 |慕容吹雪                            |
# |3 |慕容吹雪3-慕容吹雪-慕容吹雪3|慕容吹雪3                           |
# |4 |慕容吹雪4           |                                |
# +--+----------------+--------------------------------+



#  left left(name,1,instr(name,'-')-1)

select id ,name  ,left(name,instr(name,'-')-1)  from girl where id in (1,2,3,4)  ;

# +--+----------------+----------------------------+
# |id|name            |left(name,instr(name,'-')-1)|
# +--+----------------+----------------------------+
# |1 |慕容吹雪-慕容博-慕容吹雪   |慕容吹雪                        |
# |2 |慕容吹雪-慕容吹雪-慕容吹雪2 |慕容吹雪                        |
# |3 |慕容吹雪3-慕容吹雪-慕容吹雪3|慕容吹雪3                       |
# |4 |慕容吹雪4           |                            |
# +--+----------------+----------------------------+



# 元数据
# +--+----------------+
# |id|name            |
# +--+----------------+
# |1 |慕容吹雪-慕容博-慕容吹雪   |
# |2 |慕容吹雪-慕容吹雪-慕容吹雪2 |
# |3 |慕容吹雪3-慕容吹雪-慕容吹雪3|
# |4 |慕容吹雪4           |
# +--+----------------+

#  截取  最后 一个


 #  (right :下表是从右边开始的,从 1开始 ,找到第一个 ‘-’ 的下标,)

select id ,name  , instr(name,'-'),right(name,instr(name,'-')-1)  from girl where id in (1,2,3,4)  ;

# +--+----------------+---------------+-----------------------------+
# |id|name            |instr(name,'-')|right(name,instr(name,'-')-1)|
# +--+----------------+---------------+-----------------------------+
# |1 |慕容吹雪-慕容博-慕容吹雪   |5              |慕容吹雪                         |
# |2 |慕容吹雪-慕容吹雪-慕容吹雪2 |5              |容吹雪2                         |
# |3 |慕容吹雪3-慕容吹雪-慕容吹雪3|6              |慕容吹雪3                        |
# |4 |慕容吹雪4           |0              |                             |
# +--+----------------+---------------+-----------------------------+

1. 小数点位数的处理:format 函数(date_format 是处理日期的)



# format 函数保留小数点 位数 format(123456.789,2)
#        FORMAT(100.3111,0);//取整
# FORMAT函数返回一个字符串值

select  score  from exam_record;

# +-----+
# |score|
# +-----+
# |80   |
# |81   |
# |84   |
# |89   |
# |NULL |
# |NULL |
# |87   |
# |90   |
# |NULL |
# |50   |
# +-----+

# 保留整数
select    FORMAT(score,0)  from exam_record;

# +---------------+
# |FORMAT(score,0)|
# +---------------+
# |80             |
# |81             |
# |84             |
# |89             |
# |NULL           |
# |NULL           |
# |87             |
# |90             |
# |NULL           |
# |50             |
# +---------------+

#  保留小数点一位

select  FORMAT(score,1)  from exam_record;

# +---------------+
# |FORMAT(score,1)|
# +---------------+
# |80.0           |
# |81.0           |
# |84.0           |
# |89.0           |
# |NULL           |
# |NULL           |
# |87.0           |
# |90.0           |
# |NULL           |
# |50.0           |
# +---------------+



#  保留小数点二位

select  FORMAT(score,2)  from exam_record;

# +---------------+
# |FORMAT(score,2)|
# +---------------+
# |80.00          |
# |81.00          |
# |84.00          |
# |89.00          |
# |NULL           |
# |NULL           |
# |87.00          |
# |90.00          |
# |NULL           |
# |50.00          |
# +---------------+

1. 获取当月的最后一天,获取当月有几天,获取某一天的月份有几天


# mysql中LAST_DAY()函数是取某个月最后一天的日期。

# 获取当月最后一天
select last_day(curdate()) ;
# +-------------------+
# |last_day(curdate())|
# +-------------------+
# |2022-04-30         |
# +-------------------+


# 获取当月有几天
select  date_format(last_day(curdate()), '%d')  ;

# +--------------------------------------+
# |date_format(last_day(curdate()), '%d')|
# +--------------------------------------+
# |30                                    |
# +--------------------------------------+

select  date_format(last_day('2022-01-08') , '%d')  ;
# +------------------------------------------+
# |date_format(last_day('2022-01-08') , '%d')|
# +------------------------------------------+
# |31                                        |
# +------------------------------------------+

1. 一张表 两个字段前缀是否一样

# 判断 一张表 两个字段的前缀是否一样 substr
select * from girl ;

# +--+-----+------+
# |id|name |boy_id|
# +--+-----+------+
# |1 |慕容吹雪 |1     |
# |2 |慕容吹雪2|1     |
# |3 |慕容吹雪3|3     |
# |4 |慕容吹雪4|3     |
# |5 |慕容吹雪5|3     |
# |6 |慕容吹雪6|NULL  |
# |7 |D    |NULL  |
# |88|C    |8     |
# +--+-----+------+

select  name  from girl ;

# +-----+
# |name |
# +-----+
# |慕容吹雪 |
# |慕容吹雪2|
# |慕容吹雪3|
# |慕容吹雪4|
# |慕容吹雪5|
# |慕容吹雪6|
# |D    |
# |C    |
# +-----+

# substr(a,index,length) index  从1开始,可以负数,不可以0,length 要截取长度


select id , substr(name,1,4)   from girl ;
# +--+----------------+
# |id|substr(name,1,4)|
# +--+----------------+
# |1 |慕容吹雪            |
# |2 |慕容吹雪            |
# |3 |慕容吹雪            |
# |4 |慕容吹雪            |
# |5 |慕容吹雪            |
# |6 |慕容吹雪            |
# |7 |D               |
# |88|C               |
# +--+----------------+


select id , substr(name,2,3)   from girl ;

# +--+----------------+
# |id|substr(name,2,3)|
# +--+----------------+
# |1 |容吹雪             |
# |2 |容吹雪             |
# |3 |容吹雪             |
# |4 |容吹雪             |
# |5 |容吹雪             |
# |6 |容吹雪             |
# |7 |                |
# |88|                |
# +--+----------------+


select id , substr(name,2,4)   from girl ;

# +--+----------------+
# |id|substr(name,2,4)|
# +--+----------------+
# |1 |容吹雪             |
# |2 |容吹雪2            |
# |3 |容吹雪3            |
# |4 |容吹雪4            |
# |5 |容吹雪5            |
# |6 |容吹雪6            |
# |7 |                |
# |88|                |
# +--+----------------+

# 进阶: id 前几位一样的数据
select id  from girl where  substr(id,1,1)  =  substr(boy_id,1,1)  ;
# +--+
# |id|
# +--+
# |1 |
# |3 |
# |88|
# +--+

left(str,length)


# left(str,length) 函数: 左边开始的长度为 length 的子字符串


select  * from girl ;

# +--+----------------+------+-----+
# |id|name            |boy_id|name0|
# +--+----------------+------+-----+
# |1 |慕容吹雪-慕容博-慕容吹雪   |1     |慕容吹雪 |
# |2 |慕容吹雪-慕容吹雪-慕容吹雪2 |1     |慕容吹雪2|
# |3 |慕容吹雪3-慕容吹雪-慕容吹雪3|3     |慕容吹雪3|
# |4 |慕容吹雪4           |3     |NULL |
# |5 |慕容吹雪5           |3     |NULL |
# |6 |慕容吹雪6           |NULL  |NULL |
# |7 |D               |NULL  |NULL |
# |88|C               |8     |NULL |
# |99|慕容吹雪            |1     |慕容吹雪 |
# +--+----------------+------+-----+


select  left(name,4) from girl ;
# +------------+
# |left(name,4)|
# +------------+
# |慕容吹雪        |
# |慕容吹雪        |
# |慕容吹雪        |
# |慕容吹雪        |
# |慕容吹雪        |
# |慕容吹雪        |
# |D           |
# |C           |
# |慕容吹雪        |
# +------------+

1. 字符串截取除了 substr

还有substring_index():适用场景 有分隔符的场景下用

  substring_index()函数用来截取字符串
 
      substring_index(str,delim,count)
      str:要处理的字符串
      delim:分隔符
      count:计数
   如果count是正数,那么就是从左往右数,第N个分隔符的左边的全部内容,
 
   相反,如果是负数,那么就是从右边开始数,第N个分隔符右边的所有内容。
drop table if exists user_submit;
CREATE TABLE `user_submit` (
`id` int NOT NULL,
`device_id` int NOT NULL,
`profile` varchar(100) NOT NULL,
`blog_url` varchar(100) NOT NULL
);
INSERT INTO user_submit VALUES(1,2138,'180cm,75kg,27,male','http:/url/bisdgboy777');
INSERT INTO user_submit VALUES(1,3214,'165cm,45kg,26,female','http:/url/dkittycc');
INSERT INTO user_submit VALUES(1,6543,'178cm,65kg,25,male','http:/url/tigaer');
INSERT INTO user_submit VALUES(1,4321,'171cm,55kg,23,female','http:/url/uhsksd');
INSERT INTO user_submit VALUES(1,2131,'168cm,45kg,22,female','http:/url/sysdney');


select * from user_submit ;

# +--+---------+--------------------+---------------------+
# |id|device_id|profile             |blog_url             |
# +--+---------+--------------------+---------------------+
# |1 |2138     |180cm,75kg,27,male  |http:/url/bisdgboy777|
# |1 |3214     |165cm,45kg,26,female|http:/url/dkittycc   |
# |1 |6543     |178cm,65kg,25,male  |http:/url/tigaer     |
# |1 |4321     |171cm,55kg,23,female|http:/url/uhsksd     |
# |1 |2131     |168cm,45kg,22,female|http:/url/sysdney    |
# +--+---------+--------------------+---------------------+

# 运营想要统计每个年龄的用户分别有多少参赛者,请取出相应结果
select  profile from user_submit ;
# +--------------------+
# |profile             |
# +--------------------+
# |180cm,75kg,27,male  |
# |165cm,45kg,26,female|
# |178cm,65kg,25,male  |
# |171cm,55kg,23,female|
# |168cm,45kg,22,female|
# +--------------------+


# 法① :
select  substr(profile,12,2) age ,count(1) number from user_submit group by  substr(profile,12,2);

# 法② :
#
# substring_index()函数用来截取字符串
#
#     substring_index(str,delim,count)
#     str:要处理的字符串
#     delim:分隔符
#     count:计数
#  如果count是正数,那么就是从左往右数,第N个分隔符的左边的全部内容,
#
# 相反,如果是负数,那么就是从右边开始数,第N个分隔符右边的所有内容。
select  substring_index(profile,',',3) age ,count(1) number from user_submit group by substring_index(profile,',',3);

# /+-------------+------+
# |age          |number|
# +-------------+------+
# |180cm,75kg,27|1     |
# |165cm,45kg,26|1     |
# |178cm,65kg,25|1     |
# |171cm,55kg,23|1     |
# |168cm,45kg,22|1     |
# +-------------+------+

# 截取尾 
select substring_index(substring_index(profile, ',', 3), ',', -1) age, count(1) number
from user_submit
group by substring_index(substring_index(profile, ',', 3), ',', -1);

# +---+------+
# |age|number|
# +---+------+
# |27 |1     |
# |26 |1     |
# |25 |1     |
# |23 |1     |
# |22 |1     |
# +---+------+

#  从一个字段中 比如  算法,medium,80   的字段 ,截取 出  算法,medium,80 作为三列的字段
#  技巧: substring_index(,,) ,算法  和 80 是首 和 尾 ,很容易截取, medium 在中间不好截取,技巧 先截取 算法,medium 作为 字符串 A ,然后 对 字符串 A截取尾
drop table if exists examination_info,exam_record;
CREATE TABLE examination_info (
    id int PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    exam_id int UNIQUE NOT NULL COMMENT '试卷ID',
    tag varchar(32) COMMENT '类别标签',
    difficulty varchar(8) COMMENT '难度',
    duration int NOT NULL COMMENT '时长',
    release_time datetime COMMENT '发布时间'
)CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO examination_info(exam_id,tag,difficulty,duration,release_time) VALUES
  (9001, '算法', 'hard', 60, '2020-01-01 10:00:00'),
  (9002, '算法', 'hard', 80, '2020-01-01 10:00:00'),
  (9003, 'SQL', 'medium', 70, '2020-01-01 10:00:00'),
  (9004, '算法,medium,80','', 0, '2020-01-01 10:00:00');


select * from examination_info;
# +--+-------+------------+----------+--------+-------------------+
# |id|exam_id|tag         |difficulty|duration|release_time       |
# +--+-------+------------+----------+--------+-------------------+
# |1 |9001   |算法          |hard      |60      |2020-01-01 10:00:00|
# |2 |9002   |算法          |hard      |80      |2020-01-01 10:00:00|
# |3 |9003   |SQL         |medium    |70      |2020-01-01 10:00:00|
# |4 |9004   |算法,medium,80|          |0       |2020-01-01 10:00:00|
# +--+-------+------------+----------+--------+-------------------+

# 现有试卷信息表examination_info(exam_id试卷ID, tag试卷类别, difficulty试卷难度, duration考试时长, release_time发布时间)

# 录题同学有一次手误将部分记录的试题类别tag、难度、时长同时录入到了tag字段,请帮忙找出这些录错了的记录,并拆分后按正确的列类型输出。
#
# 由示例数据结果输出如下:

# +-------+---+----------+--------+
# |exam_id|tag|difficulty|duration|
# +-------+---+----------+--------+
# |9004   |算法 |medium    |80      |
# +-------+---+----------+--------+


select tag from examination_info where instr(tag,',') > 1 ;


# substring_index 也可以截取  算法,medium,80 到 medium:先截取 算法,medium,然后   算法,medium 从右面获取  medium

#  instr  or like 都可以

# 先获取  算法,medium
select substring_index(tag,',',2)     from examination_info where instr(tag,',') > 1 ;

# +--------------------------+
# |substring_index(tag,',',2)|
# +--------------------------+
# |算法,medium                 |
# +--------------------------+

# 在 算法,medium    基础上获取  medium

select substring_index(substring_index(tag,',',2) ,',',-1)      from examination_info where instr(tag,',') > 1 ;

# +---------------------------------------------------+
# |substring_index(substring_index(tag,',',2) ,',',-1)|
# +---------------------------------------------------+
# |medium                                             |
# +---------------------------------------------------+


# 拼接结果
select exam_id, substring_index(tag,',',1) tag, substring_index(substring_index(tag, ',', 2), ',', -1) difficulty,  substring_index(tag,',',-1) duration
from examination_info
where instr(tag, ',') > 1;



1.

MYSQL中coalesce函数处理 null 值 

# MYSQL中coalesce函数的用法(类似于case when then 不过 是处理 null的)
# coalesce():返回参数中的第一个非空表达式(从左向右依次类推)
# coalesce	英[ˌkəʊəˈles] 美[ˌkoʊəˈles]

# coalesce():返回参数中的第一个非空表达式(从左向右依次类推);


#  返回4
select coalesce(null,4,5);

# 返回3
select coalesce(null,null,3);

# 返回1
select coalesce(1,2,3);
# 如果传入的参数所有都是null,则返回null,比如
# 相当于case when  then
SELECT COALESCE(NULL, NULL, NULL, NULL);
-- Return NULL

# 这个参数使用的场合为:假如某个字段默认是null,你想其返回的不是null,而是比如0或其他值,可以使用这个函数


select  * from girl ;
# +--+----+------+
# |id|name|boy_id|
# +--+----+------+
# |1 |A   |1     |
# |2 |B   |1     |
# |3 |C   |3     |
# |4 |C   |3     |
# |5 |C   |3     |
# |6 |D   |NULL  |
# |7 |D   |NULL  |
# |8 |C   |2     |
# +--+----+------+
# 需求:  将 boy_id 是 null的 值 ,改成 -1 显示
SELECT COALESCE(boy_id,-1) as value from girl;
# +-----+
# |value|
# +-----+
# |1    |
# |1    |
# |3    |
# |3    |
# |3    |
# |-1   |
# |-1   |
# |2    |
# +-----+

# case when 实现

select case when boy_id is null then -1 else boy_id end boy_id
from girl;

# +------+
# |boy_id|
# +------+
# |1     |
# |1     |
# |3     |
# |3     |
# |3     |
# |-1    |
# |-1    |
# |2     |
# +------+

1. 两个日期相差几天

# MySQL中的两个时间函数,用来做两个时间之间的对比(比较两个日期相差几天 DATEDIFF)
# TIMESTAMPDIFF,(如果当期时间和之前时间的分钟数相比较。大于1天,即等于1;小于1天,则等于0)


#
# DATEDIFF,(只按2016-11-16计算,不会加小时分钟数,按天计算)
# 2021-05-22 10:13:42
 select DATEDIFF(NOW(),'2016-11-16 17:10:52');
# +-------------------------------------+
# |DATEDIFF(NOW(),'2016-11-16 17:10:52')|
# +-------------------------------------+
# |1648                                 |
# +-------------------------------------+

 select DATEDIFF('2021-05-22 10:13:42','2016-11-16 17:10:52');

# +-----------------------------------------------------+
# |DATEDIFF('2021-05-22 10:13:42','2016-11-16 17:10:52')|
# +-----------------------------------------------------+
# |1648                                                 |
# +-----------------------------------------------------+

 select DATEDIFF('2021-05-22 10:13:42','2021-05-21 17:10:52');
# +-----------------------------------------------------+
# |DATEDIFF('2021-05-22 10:13:42','2021-05-21 17:10:52')|
# +-----------------------------------------------------+
# |1                                                    |
# +-----------------------------------------------------+
 select DATEDIFF('2021-05-22 10:13:42','2021-05-23 17:10:52');
# +-----------------------------------------------------+
# |DATEDIFF('2021-05-22 10:13:42','2021-05-23 17:10:52')|
# +-----------------------------------------------------+
# |-1                                                   |
# +-----------------------------------------------------+

 select DATEDIFF('2021-05-22 10:13:42','2021-05-31 17:10:52');
# +-----------------------------------------------------+
# |DATEDIFF('2021-05-22 10:13:42','2021-05-31 17:10:52')|
# +-----------------------------------------------------+
# |-9                                                   |
# +-----------------------------------------------------+
# 当前时间 2021-05-22
 select TIMESTAMPDIFF(DAY,'2021-11-16 10:13:42',NOW());
# +----------------------------------------------+
# |TIMESTAMPDIFF(DAY,'2021-11-16 10:13:42',NOW())|
# +----------------------------------------------+
# |-178                                          |
# +----------------------------------------------+

 select TIMESTAMPDIFF(DAY,'2021-05-22 10:13:42',NOW());
# +----------------------------------------------+
# |TIMESTAMPDIFF(DAY,'2021-05-22 10:13:42',NOW())|
# +----------------------------------------------+
# |0                                             |
# +----------------------------------------------+


 select TIMESTAMPDIFF(DAY,'2021-05-22 10:13:42','2021-05-22 10:13:46');

# +--------------------------------------------------------------+
# |TIMESTAMPDIFF(DAY,'2021-05-22 10:13:42','2021-05-22 10:13:46')|
# +--------------------------------------------------------------+
# |0                                                             |
# +--------------------------------------------------------------+


# mysql  日期的比较(两个时间之间差了几年,几个季度,几个月,几个周,几个小时,几分钟等)

# datediff(date1,date2):两个日期相减 date1 - date2,返回天数。

# TIMESTAMPDIFF(interval,datetime_expr1,datetime_expr2)

# 函数共有三个参数,三个参数的取值分别是:
#
# interval:比较的类型,可取值FRAC_SECOND、SECOND、 MINUTE、 HOUR、 DAY、 WEEK、 MONTH、 QUARTER 或 YEAR
#
# datetime_expr1和datetime_expr2是待比较的两个时间,用后面的时间减去前面的时间

# demo

select datediff('2008-08-08', '2008-08-01');

# +------------------------------------+
# |datediff('2008-08-08', '2008-08-01')|
# +------------------------------------+
# |7                                   |
# +------------------------------------+


select TIMESTAMPDIFF(DAY,'2008-08-01','2008-08-08') ;

# +--------------------------------------------+
# |TIMESTAMPDIFF(DAY,'2008-08-01','2008-08-08')|
# +--------------------------------------------+
# |7                                           |
# +--------------------------------------------+

# 差几小时
select TIMESTAMPDIFF(HOUR,'2008-08-01 22:00:00','2008-08-01 23:01:00') ;

# +---------------------------------------------------------------+
# |TIMESTAMPDIFF(HOUR,'2008-08-01 22:00:00','2008-08-01 23:00:00')|
# +---------------------------------------------------------------+
# |1                                                              |
# +---------------------------------------------------------------+


# # 差几分钟时
select TIMESTAMPDIFF(MINUTE,'2008-08-01 22:00:00','2008-08-01 23:01:00') ;

# +-----------------------------------------------------------------+
# |TIMESTAMPDIFF(MINUTE,'2008-08-01 22:00:00','2008-08-01 23:01:00')|
# +-----------------------------------------------------------------+
# |61                                                               |
# +-----------------------------------------------------------------+

1. 加几天 dateadd


#  加七天,不包括  created_time这天
select created_time ,
DATE_ADD(created_time, INTERVAL 7 DAY) from boys;
# +-------------------+--------------------------------------+
# |created_time       |DATE_ADD(created_time, INTERVAL 7 DAY)|
# +-------------------+--------------------------------------+
# |1000-05-01 17:43:43|1000-05-08 17:43:43                   |
# |1005-05-21 17:43:49|1005-05-28 17:43:49                   |
# |1101-05-21 17:43:52|1101-05-28 17:43:52                   |
# |1121-05-21 17:43:55|1121-05-28 17:43:55                   |
# |1190-05-21 17:43:58|1190-05-28 17:43:58                   |
# |2021-05-22 17:44:01|2021-05-29 17:44:01                   |
# |2040-05-22 17:44:01|2040-05-29 17:44:01                   |
# |2050-05-22 17:44:01|2050-05-29 17:44:01                   |
# |1901-05-22 17:44:01|1901-05-29 17:44:01                   |
# |1899-05-21 17:43:58|1899-05-28 17:43:58                   |
# +-------------------+--------------------------------------+

1. ifnull


update girls set is_deleted = '' where id = 4 ;

select is_deleted  as is_deleted from girls;
# +----------+
# |is_deleted|
# +----------+
# |1         |
# |0         |
# |0         |
# |          |
# +----------+

#  is_deleted 这个字段如果是 null or  ‘’ ,则赋值为  0
select ifnull(is_deleted,0) as is_deleted from girls;
# +----------+
# |is_deleted|
# +----------+
# |1         |
# |0         |
# |0         |
# |0         |
# +----------+

1. case when then else  end 

   可以根据枚举值转文字;可以根据判断是否为null,null的话给默认值(类似Oracle 的isNul函数);可以批量更新,这个可以解决一些奇奇怪怪的需求

  eg:select case when 如果pc.taxNo字段不为空,显示pc.taxNo。如果pc.taxNo字段为空,显示'金额合计'。end结束

一:分析函数


#  由一个sql引入的分析函数 sum() over()
#  60
#  按照salary的累计和running_total,其中running_total为前N个当前( to_date = '9999-01-01')员工的salary累计和,其他以此类推。
#  具体结果如下Demo展示。
CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));


select * from salaries_60;
# +------+------+----------+----------+
# |emp_no|salary|from_date |to_date   |
# +------+------+----------+----------+
# |1     |100   |2021-01-23|9999-01-01|
# |2     |1000  |2021-01-23|9999-01-01|
# |3     |10000 |2021-01-23|9999-01-01|
# |4     |500   |2021-01-23|9999-01-01|
# |5     |5000  |2021-01-23|9999-01-01|
# +------+------+----------+----------+

# running_total

select s.emp_no,
       s.salary,
       row_number() over (order by emp_no asc)                                                        as rn,
       (select sum(s2.salary), row_number() over (order by emp_no asc) as rn2 from salaries_60 s2 where s2.rn2 <= s.rn) as running_total
from salaries_60 s;

# 识别不了别名 ,进行改写 (下面是个错误的sql)

select A.emp_no, A.salary, A.rn ,
       (
          select  B.sm from (
                         select sum(s2.salary) as  sm , row_number() over (order by emp_no asc) as rn2 from salaries_60 s2
                          ) B where B.rn2 <= A.rn

           )  as running_total
from (
         select s.emp_no,
                s.salary,
                row_number() over (order by emp_no asc) as rn
         from salaries_60 s
     ) A ;

# +------+------+--+-------------+
# |emp_no|salary|rn|running_total|
# +------+------+--+-------------+
# |1     |100   |1 |16600        |
# |2     |1000  |2 |16600        |
# |3     |10000 |3 |16600        |
# |4     |500   |4 |16600        |
# |5     |5000  |5 |16600        |
# +------+------+--+-------------+


#避免  is incompatible with sql_mode=only_full_group_by  这个错误
set session sql_mode='';
select  B.sm from (
 select sum(s2.salary) as  sm , row_number() over (order by emp_no asc) as rn2 from salaries_60 s2
                           ) B where B.rn2 <= 3 ;



select sum(s2.salary)  from salaries_60 s2;
# +--------------+
# |sum(s2.salary)|
# +--------------+
# |16600         |
# +--------------+
# 下面sql错误:sum(salary) 会对  salaries_60 表的所有数据求和,导致 B的  sm 是一个常量
select sm
from (
         select sum(salary) sm, row_number() over (order by emp_no asc) rn2 from salaries_60 s2
     ) B
where B.rn2 <= 2;
# +-----+
# |sm   |
# +-----+
# |16600|
# +-----+

# 正确解法
select sum(sm)
from (
         select salary sm, row_number() over (order by emp_no asc) rn2 from salaries_60 s2
     ) B
# 识别不了  s2  ,所以加个套 ,where B.rn2 <= 2
where B.rn2 <= 2;
# +-------+
# |sum(sm)|
# +-------+
# |1100   |
# +-------+

# 正确sql
select A.emp_no, A.salary, A.rn ,
       (
          select  sum(sm) from (
                         select s2.salary as  sm , row_number() over (order by emp_no asc) as rn2 from salaries_60 s2
                          ) B where B.rn2 <= A.rn

           )  as running_total
from (
         select s.emp_no,
                s.salary,
                row_number() over (order by emp_no asc) as rn
         from salaries_60 s
     ) A ;
# +------+------+--+-------------+
# |emp_no|salary|rn|running_total|
# +------+------+--+-------------+
# |1     |100   |1 |100          |
# |2     |1000  |2 |1100         |
# |3     |10000 |3 |11100        |
# |4     |500   |4 |11600        |
# |5     |5000  |5 |16600        |
# +------+------+--+-------------+

# 法二:(用分析函数)
SELECT emp_no, salary, SUM(salary) OVER (ORDER BY emp_no) AS running_total
FROM salaries
WHERE to_date = '9999-01-01';

# mysql的分析函数
# 分析函数之连续求和sum(…) over(…)
# 分析函数之排序值rank()和dense_rank()
# 分析函数之排序后顺序号row_number()
# 分析函数之取上下行数据lag()和lead()
# 分析函数和聚合函数的区别
# 普通的聚合函数用group by分组,每个分组返回一个统计值,
# 分析函数采用partition by分组,并且每组每行都可以返回一个统计值。
# 语法
# FUNCTION_NAME(<参数>,…)
# OVER (<PARTITION BY 表达式,…>
# 	  <ORDER BY 表达式 <ASC DESC>
# 	  <NULLS FIRST NULLS LAST>>
# 	  <WINDOWING子句>)
# 分析函数带有一个开窗函数over(),包含三个分析子句:
#
# 分组(partition by)
# 排序(order by)
# 窗口(rows)
# 规则:
# sum(...) over( ),对所有行求和
#
# sum(...) over( order by ... ), 连续求和
#
#
# sum(...) over( partition by... ),同组内所行求和
#
# sum(...) over( partition by... order by ... ),同第1点中的排序求和原理,只是范围限制在组内
# 总结
# 在"… from emp;"后面不要加order by 子句,使用的分析函数的(partition by deptno order by sal)
# 里已经有排序的语句了,如果再在句尾添加排序子句,一致倒罢了,不一致,结果就令人费劲了
# ① 连续求和
# 连续求和分析函数 sum(…) over(…)
# 【功能】连续求和分析函数
# 【参数】具体参示例
SELECT *
FROM salaries_60 s ;
# +------+------+----------+----------+---+
# |emp_no|salary|from_date |to_date   |sex|
# +------+------+----------+----------+---+
# |1     |100   |2021-01-23|9999-01-01|0  |
# |2     |1000  |2021-01-23|9999-01-01|1  |
# |3     |10000 |2021-01-23|9999-01-01|1  |
# |4     |500   |2021-01-23|9999-01-01|0  |
# |5     |5000  |2021-01-23|9999-01-01|1  |
# +------+------+----------+----------+---+
# 对所有行求和
SELECT s.emp_no, s.salary , sum(s.salary) over() as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so   |
# +------+------+-----+
# |1     |100   |16600|
# |2     |1000  |16600|
# |3     |10000 |16600|
# |4     |500   |16600|
# |5     |5000  |16600|
# +------+------+-----+


# 连续求和(连续的话就得加order by)
SELECT s.emp_no, s.salary , sum(s.salary) over(order by emp_no) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so   |
# +------+------+-----+
# |1     |100   |100  |
# |2     |1000  |1100 |
# |3     |10000 |11100|
# |4     |500   |11600|
# |5     |5000  |16600|
# +------+------+-----+

#  group by 后,每个组 返回一个结果
SELECT sum(s.salary)
FROM salaries_60 s group by  s.emp_no, s.salary;
# +-------------+
# |sum(s.salary)|
# +-------------+
# |100          |
# |1000         |
# |10000        |
# |500          |
# |5000         |
# +-------------+
#  group by 后,每个组 返回一个结果
set session sql_mode='';
SELECT sum(s.salary)
FROM salaries_60 s group by  s.salary;
# +-------------+
# |sum(s.salary)|
# +-------------+
# |100          |
# |1000         |
# |10000        |
# |500          |
# |5000         |
# +-------------+

SELECT sum(s.salary)
FROM salaries_60 s ;
# +-------------+
# |sum(s.salary)|
# +-------------+
# |16600        |
# +-------------+



# sum(sal) over (order by deptno,ename) 按emp_no 和 sex “连续”求总和
SELECT s.emp_no, s.salary , sum(s.salary) over(order by emp_no ,sex) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so   |
# +------+------+-----+
# |1     |100   |100  |
# |2     |1000  |1100 |
# |3     |10000 |11100|
# |4     |500   |11600|
# |5     |5000  |16600|
# +------+------+-----+

# 同组内求和
SELECT s.emp_no, s.salary , sum(s.salary) over(partition by sex ) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so   |
# +------+------+-----+
# |1     |100   |600  |
# |4     |500   |600  |
# |2     |1000  |16000|
# |3     |10000 |16000|
# |5     |5000  |16000|
# +------+------+-----+

# 同组内连续求和
# sum(...) over( partition by... order by ... ),同第1点中的排序求和原理,只是范围限制在组内
SELECT s.emp_no, s.salary , sum(s.salary) over(partition by sex order by emp_no) as so
FROM salaries_60 s ;
# +------+------+-----+
# |emp_no|salary|so   |
# +------+------+-----+
# |1     |100   |100  |
# |4     |500   |600  |
# |2     |1000  |1000 |
# |3     |10000 |11000|
# |5     |5000  |16000|
# +------+------+-----+

# 即sex从大到小排列,sex组里各员工的薪水从高到低排列,累计和的规则不变(组内连续求和和 总体连续求和)。
SELECT
       s.emp_no,
       s.salary ,
       sum(s.salary) over(partition by sex order by salary desc) as sex_gro_sum ,
         sum(s.salary) over(order by salary desc) as all_sum
FROM salaries_60 s ;
# +------+------+-----------+-------+
# |emp_no|salary|sex_gro_sum|all_sum|
# +------+------+-----------+-------+
# |3     |10000 |10000      |10000  |
# |5     |5000  |15000      |15000  |
# |2     |1000  |16000      |16000  |
# |4     |500   |500        |16500  |
# |1     |100   |600        |16600  |
# +------+------+-----------+-------+

SELECT
       s.emp_no,
       s.salary ,
       sum(s.salary) over(partition by sex order by emp_no asc ,salary desc) as sex_gro_sum ,
         sum(s.salary) over(order by emp_no asc ,salary desc  ) as all_sum
FROM salaries_60 s ;
# +------+------+-----------+-------+
# |emp_no|salary|sex_gro_sum|all_sum|
# +------+------+-----------+-------+
# |1     |100   |100        |100    |
# |2     |1000  |1000       |1100   |
# |3     |10000 |11000      |11100  |
# |4     |500   |600        |11600  |
# |5     |5000  |16000      |16600  |
# +------+------+-----------+-------+



一:

w3school:SQL MID() 函数

一.

WITH as  

①做子查询

  

WITH recursion_name (name, short_name, code, parent_code, level, flag) AS
         (SELECT T1.name, T1.short_name, T1.code, T1.parent_code, T1.level, T1.flag
          from sys_region T1
          where T1.code = '370000000000')

select *
from recursion_name;

②sql 递归,生成一行一行数据集,最后用  union all  拼成一个大的结果集

 -- 查询子节点   不含自己 (recursion 递归)
 WITH RECURSIVE recursion (name, short_name, code, parent_code, level, flag) AS
(
  SELECT T1.name, T1.short_name, T1.code, T1.parent_code, T1.level, T1.flag
	  from sys_region T1
	 where T1.code='370000000000'
  UNION ALL
  SELECT T2.name, T2.short_name, T2.code, T2.parent_code, T2.level, T2.flag
    from sys_region T2,
         recursion T3
	 WHERE T2.parent_code=T3.code
)
SELECT  T.name, T.short_name, T.code, T.parent_code, T.level, T.flag
  FROM recursion T
 where T.code!='370000000000' ;

 分析:处理流程:

    ① 

 union all  上面的sql

SELECT T1.id, T1.name, T1.short_name, T1.code, T1.parent_code, T1.level, T1.flag
 from sys_region T1
where T1.code='370171401000' 
+---+----+----------+------------+------------+-----+----+
|id |name|short_name|code        |parent_code |level|flag|
+---+----+----------+------------+------------+-----+----+
|181|章锦街道|章锦街道      |370171401000|370171000000|4    |0   |
+---+----+----------+------------+------------+-----+----+

是最开始要处理的数据集

②  union all 下面的sql 是将   ① 中生成的结果  逐条遍历

  例如第一条:

SELECT T2.id, T2.name, T2.short_name, T2.code, T2.parent_code, T2.level, T2.flag
  from sys_region T2, recursion T3
WHERE T2.code= “370171000000”

③ 执行Union all 和 之前的原始 sql 拼接成 两条记录

④ 然后刚刚生成的 新记录调用 回调函数 recursion ,执行 union all  下的sql 方法,得到的结果集再去 union  all

⑤ 每生成新的记录就会 调用  recursion,直到不会产生新的记录

结果集为:

+--+-----------+----------+------------+------------+-----+----+
|id|name       |short_name|code        |parent_code |level|flag|
+--+-----------+----------+------------+------------+-----+----+
|16|济南高新技术产业开发区|高新区       |370171000000|370100000000|3    |0   |
+--+-----------+----------+------------+------------+-----+----+

1. with as 多个 临时表: with 临时表A as (select 子查询1),临时表B as (select 子查2 可以用到临时表A) select * from 临时表B inner  |left | right join on

with A as (
select boys.id as id, boys.name, boys.sex, boys.age from boys inner join girl g on boys.id = g.boy_id

),
B as (
select A.id, A.name, A.sex, A.age from A where   A.id = 3

    )
select B.* from B ;

查询结果如下: 

+--+----+---+---+
|id|name|sex|age|
+--+----+---+---+
|3 |慕容垂 |男  |44 |
|3 |慕容垂 |男  |44 |
|3 |慕容垂 |男  |44 |
|3 |慕容垂 |男  |44 |
|3 |慕容垂 |男  |44 |
+--+----+---+---+

一.

DATEDIFF() 

#  DATEDIFF() 函数计算两个日期之间的间隔天数
SELECT DATEDIFF('2017-11-10','2017-11-29') AS COL1;
# +----+
# |COL1|
# +----+
# |-19 |
# +----+
SELECT DATEDIFF('2017-11-29','2017-11-20') AS COL1;
# +----+
# |COL1|
# +----+
# |9   |
# +----+

一.


# DATE_FORMAT() 函数用于以不同的格式显示日期/时间数据。
# DATE_FORMAT(date,format)函数则是把数据库的日期转换为对应的字符串格式
# 语法: DATE_FORMAT(date,format)
select DATE_FORMAT(NOW(),'%Y-%m-%d') as date_format;
# +-----------+
# |date_format|
# +-----------+
# |2021-01-16 |
# +-----------+

select DATE_FORMAT(NOW(),'%Y年%m月%d日') as date_format;
# +-----------+
# |date_format|
# +-----------+
# |2021年01月16日|
# +-----------+

# STR_TO_DATE(str,format)函数是将时间格式的字符串(str),按照所提供的显示格式(format)转换为DATETIME类型的值

SELECT STR_TO_DATE('2021-01-16 10:20:30','%Y-%m-%d %H:%i:%s') AS result;
# +-------------------+
# |result             |
# +-------------------+
# |2021-01-16 10:20:30|
# +-------------------+

SELECT STR_TO_DATE('2021-01-16 10:20:30','%Y-%m-%d') AS result;
# +----------+
# |result    |
# +----------+
# |2021-01-16|
# +----------+

# MID() 函数
# MID 函数用于从文本字段中提取字符
# 语法:
# SELECT MID(column_name,start[,length]) FROM table_name
#  start  必需。规定开始位置(起始值是 1)。
# length 要返回的字符数。如果省略,则 MID() 函数返回剩余文本。

select mid(name,1) from commodity_order where id = 1;
# +-----------+
# |mid(name,1)|
# +-----------+
# |置办年货       |
# +-----------+
select mid(name,1,1) from commodity_order where id = 1;
# +-------------+
# |mid(name,1,1)|
# +-------------+
# |置            |
# +-------------+
select mid(name,1,3) from commodity_order where id = 1;
# +-------------+
# |mid(name,1,3)|
# +-------------+
# |置办年          |
# +-------------+

# length() 函数
# length 函数返回文本字段中值的长度。
# 语法:SELECT LEN(column_name) FROM table_name
select length(name) from commodity_order where id = 1;
# +------------+
# |length(name)|
# +------------+
# |12          |
# +------------+

# substr 从 1 开始截取[index,截取长度
select substr(name,1) from commodity_order where id = 1;
# +--------------+
# |substr(name,1)|
# +--------------+
# |置办年货          |
# +--------------+
select substr(name,2) from commodity_order where id = 1;
# +--------------+
# |substr(name,2)|
# +--------------+
# |办年货           |
# +--------------+
select substr(name,2,2) from commodity_order where id = 1;
# +----------------+
# |substr(name,2,2)|
# +----------------+
# |办年              |
# +----------------+

# LEFT()函数是一个字符串函数,它返回具有指定长度的字符串的左边部分。
# LEFT(str,length);
select LEFT(name,2) from commodity_order where id = 1;
#+------------+
# |LEFT(name,2)|
# +------------+
# |置办          |
# +------------+

一:

length() 

replace()


# 9
select length('郭一光') ;
# 3
select length('gyg') ;
# 6
select length('10,A,B') ;

#  4
select  length(replace('10,A,B',',',''));

# 2 统计 10,A,B 出现 , 的次数
select (length('10,A,B')) - length(replace('10,A,B',',',''))  ;

一.

GROUP_CONCAT()函数
GROUP_CONCAT函数返回一个字符串结果,该结果由分组中的值连接组合而成。

常用用法:

MySQL 将查询出来的一列数据拼装成一个字符串(纵向拼接,而concat是横向拼接)

1、使用语法及特点:
GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC] [,col ...]]
[SEPARATOR str_val])
在 MySQL 中,你可以得到表达式结合体的连结值。通过使用 DISTINCT 可以排除重复值。如果希望对结果中的值进行排序,可以使用 ORDER BY 子句。
SEPARATOR 是一个字符串值,它被用于插入到结果值中。缺省为一个逗号 (","),可以通过指定 SEPARATOR "" 完全地移除这个分隔符。
可以通过变量 group_concat_max_len 设置一个最大的长度。在运行时执行的句法如下: SET [SESSION | GLOBAL] group_concat_max_len = unsigned_integer;
如果最大长度被设置,结果值被剪切到这个最大长度。如果分组的字符过长,可以对系统参数进行设置:SET @@global.group_concat_max_len=40000;

+----------+----+--------------------------+
| locus    | id | journal                  |
+----------+----+--------------------------+
| AB086827 |  1 | Unpublished              |
| AB086827 |  2 | Submitted (20-JUN-2002)  |
| AF040764 | 23 | Unpublished              |
| AF040764 | 24 | Submitted (31-DEC-1997)  |
+----------+----+--------------------------+
语句 SELECT locus,GROUP_CONCAT(id) FROM info WHERE locus IN('AB086827','AF040764') GROUP BY locus; 的返回结果为
+----------+------------------+
| locus    | GROUP_CONCAT(id) |
+----------+------------------+
| AB086827 | 1,2              |
| AF040764 | 23,24            |
+----------+------------------+

语句 SELECT locus,GROUP_CONCAT(distinct id ORDER BY id DESC SEPARATOR '_') FROM info WHERE locus IN('AB086827','AF040764') GROUP BY locus;的返回结果为
+----------+----------------------------------------------------------+
| locus    | GROUP_CONCAT(distinct id ORDER BY id DESC SEPARATOR '_') |
+----------+----------------------------------------------------------+
| AB086827 | 2_1                                                      |
| AF040764 | 24_23                                                    |
+----------+----------------------------------------------------------+

纵向拼接:

1. 对数据分组后,获取组内排名前几的记录

 思路①:rank() over()  推荐:分区后对组内的数据进行排序

       rank() over(partition by cid order by score desc)          可能取多

       dense_rank() over(partition by cid order by score desc)  可能取多

       row_number() over (partition by cid order by score desc)  推荐

思路②:不用group by 后的分组,每条记录在 组内的排名(子查询)

代码如下:

# 元数据如下:
select cid, score
from sc B
# group by cid, score # 后会有两条数据被处理掉,这两条数据 cid和score 一样,但是 sid 所属同学是不同的
order by cid, score desc;
# +---+-----+
# |cid|score|
# +---+-----+
# |01 |80.0 |
# |01 |80.0 |
# |01 |76.0 |
# |01 |70.0 |
# |01 |50.0 |
# |01 |31.0 |
# |02 |90.0 |
# |02 |89.0 |
# |02 |87.0 |
# |02 |80.0 |
# |02 |60.0 |
# |02 |30.0 |
# |03 |99.0 |
# |03 |98.0 |
# |03 |80.0 |
# |03 |80.0 |
# |03 |34.0 |
# |03 |20.0 |
# +---+-----+
# 查询各科成绩前三名的记录
#  rank() over(partition by cid order by score desc) 对数据分区后,然后对分区后的数据进行排序
# 参考:(必看)
# 思路①:分区内排序
select *, rank() over(partition by cid order by score desc) as graderank from sc;
# 18 条数据
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |1        |
# |05 |01 |76.0 |3        |
# |02 |01 |70.0 |4        |
# |04 |01 |50.0 |5        |
# |06 |01 |31.0 |6        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |03 |02 |80.0 |4        |
# |02 |02 |60.0 |5        |
# |04 |02 |30.0 |6        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# |03 |03 |80.0 |3        |
# |06 |03 |34.0 |5        |
# |04 |03 |20.0 |6        |
# +---+---+-----+---------+
select *, dense_rank() over(partition by cid order by score desc) as graderank from sc;
# 18条数据
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |1        |
# |05 |01 |76.0 |2        |
# |02 |01 |70.0 |3        |
# |04 |01 |50.0 |4        |
# |06 |01 |31.0 |5        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |03 |02 |80.0 |4        |
# |02 |02 |60.0 |5        |
# |04 |02 |30.0 |6        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# |03 |03 |80.0 |3        |
# |06 |03 |34.0 |4        |
# |04 |03 |20.0 |5        |
# +---+---+-----+---------+
select *, row_number() over (partition by cid order by score desc) as graderank from sc;
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |2        |
# |05 |01 |76.0 |3        |
# |02 |01 |70.0 |4        |
# |04 |01 |50.0 |5        |
# |06 |01 |31.0 |6        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |03 |02 |80.0 |4        |
# |02 |02 |60.0 |5        |
# |04 |02 |30.0 |6        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# |03 |03 |80.0 |4        |
# |06 |03 |34.0 |5        |
# |04 |03 |20.0 |6        |
# +---+---+-----+---------+

# graderank 表中没有字段,解析不到,所以需要加个 套,套起这个sql
select * from (select *, rank() over(partition by cid order by score desc) as graderank from sc) A
where A.graderank <= 3 ;
# 10 条数据
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |1        |
# |05 |01 |76.0 |3        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# |03 |03 |80.0 |3        |
# +---+---+-----+---------+
select * from (select *, dense_rank() over(partition by cid order by score desc) as graderank from sc) A
where A.graderank <= 3 ;
# 11 条数据
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |1        |
# |05 |01 |76.0 |2        |
# |02 |01 |70.0 |3        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# |03 |03 |80.0 |3        |
# +---+---+-----+---------+
#
select * from (select *, row_number() over(partition by cid order by score desc) as graderank from sc) A
where A.graderank <= 3 ;
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |2        |
# |05 |01 |76.0 |3        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# +---+---+-----+---------+

#  思路②

#  mysql 用Group by分组后,取每组的前几条记录的方法和理解
#  参考链接:
# https://blog.csdn.net/junzi528/article/details/84404412?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
#  转化思路:相同课程下,该同学的排名,只取第一名,第二名和第三名的
#  下面sql 语法错误: 解析不了 rk 列,需要加个 套
# select
#        Cid,
#        score ,
#        (select count(1) from sc B  where B.Cid = A.Cid  and B.score >= A.score) as rk
#
# from sc A
# where rk <=2  ;
select *
from (
         select Cid,
                score,
                (select count(1) from sc B where B.Cid = A.Cid and B.score >= A.score) as rk

         from sc A
     ) RK
where rk <= 2 order by cid;
# 结果如下:
# +---+-----+--+
# |Cid|score|rk|
# +---+-----+--+
# |01 |80.0 |2 |
# |01 |80.0 |2 |
# |02 |90.0 |1 |
# |02 |89.0 |2 |
# |03 |99.0 |1 |
# |03 |98.0 |2 |
# +---+-----+--+

# 另一种写法:思路是一样的
select Cid,
       score,
#      同一课程下这个分数的名次
       (select count(1) from sc B where B.Cid = A.Cid and B.score >= A.score) as Rk
from sc A
where (select count(1) from sc B where B.Cid = A.Cid and B.score >= A.score) <= 2
order by cid;
# +---+-----+--+
# |Cid|score|Rk|
# +---+-----+--+
# |01 |80.0 |2 |
# |01 |80.0 |2 |
# |02 |90.0 |1 |
# |02 |89.0 |2 |
# |03 |99.0 |1 |
# |03 |98.0 |2 |
# +---+-----+--+

# 改进sql如下:
select Cid,
       score
from sc A
where (select count(1) from sc B where B.Cid = A.Cid and B.score >= A.score) <= 2
order by cid;
# 结果如下:
# +---+-----+
# |Cid|score|
# +---+-----+
# |01 |80.0 |
# |01 |80.0 |
# |02 |90.0 |
# |02 |89.0 |
# |03 |99.0 |
# |03 |98.0 |
# +---+-----+


# group by

select cid ,score from sc group by cid,score  ;
# group by 可以取到各个组的最大值,cid,score 这个分组
select cid ,score, max(score) as max from sc group by cid,score;
# 结果如下:
# +---+-----+----+
# |cid|score|max |
# +---+-----+----+
# |01 |80.0 |80.0|
# |02 |90.0 |90.0|
# |03 |99.0 |99.0|
# |01 |70.0 |70.0|
# |02 |60.0 |60.0|
# |03 |80.0 |80.0|
# |02 |80.0 |80.0|
# |01 |50.0 |50.0|
# |02 |30.0 |30.0|
# |03 |20.0 |20.0|
# |01 |76.0 |76.0|
# |02 |87.0 |87.0|
# |01 |31.0 |31.0|
# |03 |34.0 |34.0|
# |02 |89.0 |89.0|
# |03 |98.0 |98.0|
# +---+-----+----+
select cid ,max(score) as max from sc group by cid;
# 结果如下:
# +---+----+
# |cid|max |
# +---+----+
# |01 |80.0|
# |02 |90.0|
# |03 |99.0|
# +---+----+
# 下面sql 语法错误 因为  A.score < max(B.score) max函数位置错误,所以考虑用子查询代替
# select B.cid,
#        max(B.score)                                                                   as max,
#        (select max(A.score) from sc A where A.Cid = B.cid and A.score < max(B.score)) as second
# from sc B
# group by B.cid;
# 注意:下面sql 设计有问题:课程1 的 80 分有两个同学,下面的sql 是按照一个处理的
# 修改完用子查询 代替 max的sql如下:
select B.cid,
       max(B.score)                            as max,
#        第二就是除了max后的最大值,所以先用 max函数,然后限制条件 加上  <   max(B.score)
       (select max(A.score)
        from sc A
#        限制同一个课程
        where A.Cid = B.cid
          and A.score < (select max(c.score)
                         from sc c
                         where c.Cid = a.cId)) as second
from sc B
group by B.cid;
# 查询结果
# +---+----+------+
# |cid|max |second|
# +---+----+------+
# |01 |80.0|76.0  |
# |02 |90.0|89.0  |
# |03 |99.0|98.0  |
# +---+----+------+
#  说明:如果取分组后,组内的前三条数据就比较麻烦了
select B.cid,
       max(B.score)                            as max,
#        第二就是除了max后的最大值,所以先用 max函数,然后限制条件 加上  <  max(B.score)
       (select max(A.score)
        from sc A
#        限制同一个课程
        where A.Cid = B.cid
          and A.score < (select max(c.score)
                         from sc c
                         where c.Cid = a.cId)) as second,
#        第三
       (select max(A.score)
        from sc A
#        限制同一个课程
        where A.Cid = B.cid
#           取出分组后,组内的第二大
          and A.score < (select min(score)
                         from (
                                  select score
                                  from sc d
                                  where d.Cid = B.cid
                                  group by score
                                  order by score desc
                                  limit 2
                              ) secondTable))  as third
from sc B
group by B.cid;
# 结果如下:
# +---+----+------+-----+
# |cid|max |second|third|
# +---+----+------+-----+
# |01 |80.0|76.0  |70.0 |
# |02 |90.0|89.0  |87.0 |
# |03 |99.0|98.0  |80.0 |
# +---+----+------+-----+
# 上例中 获取第二的sql,最后将 01 替换为 B.cid
select min(score)
                         from (
                                  select score
                                  from sc d
                                  where d.Cid = 01
                                  group by score
                                  order by score desc
                                  limit 2
                              ) secondTable;






1. rank(),dense_rank(),rownum()函数用来分区后组内排序


# 说明: rank() 函数,可以给衍生表增加一列,该列代表顺序,ps:不会改变行数
select *, rank() over(partition by cid order by score desc) as graderank from sc ;
# 结果如下:
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |01 |80.0 |1        |
# |03 |01 |80.0 |1        |
# |05 |01 |76.0 |3        |
# |02 |01 |70.0 |4        |
# |04 |01 |50.0 |5        |
# |06 |01 |31.0 |6        |
# |01 |02 |90.0 |1        |
# |07 |02 |89.0 |2        |
# |05 |02 |87.0 |3        |
# |03 |02 |80.0 |4        |
# |02 |02 |60.0 |5        |
# |04 |02 |30.0 |6        |
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |02 |03 |80.0 |3        |
# |03 |03 |80.0 |3        |
# |06 |03 |34.0 |5        |
# |04 |03 |20.0 |6        |
# +---+---+-----+---------+
select *, rank() over( order by score desc) as graderank from sc ;
# 结果如下:
# +---+---+-----+---------+
# |Sid|Cid|score|graderank|
# +---+---+-----+---------+
# |01 |03 |99.0 |1        |
# |07 |03 |98.0 |2        |
# |01 |02 |90.0 |3        |
# |07 |02 |89.0 |4        |
# |05 |02 |87.0 |5        |
# |01 |01 |80.0 |6        |
# |02 |03 |80.0 |6        |
# |03 |01 |80.0 |6        |
# |03 |02 |80.0 |6        |
# |03 |03 |80.0 |6        |
# |05 |01 |76.0 |11       |
# |02 |01 |70.0 |12       |
# |02 |02 |60.0 |13       |
# |04 |01 |50.0 |14       |
# |06 |03 |34.0 |15       |
# |06 |01 |31.0 |16       |
# |04 |02 |30.0 |17       |
# |04 |03 |20.0 |18       |
# +---+---+-----+---------+
select Count(*)  from sc ;
# +--------+
# |Count(*)|
# +--------+
# |18      |
# +--------+

# 测试
create table students_rank(
	id int(4)  auto_increment primary key,
	name varchar(50) not null,
	score int(4) not null
	);
insert into students_rank(name,score) values('curry', 100),
	('klay', 99),
	('KD', 100),
	('green', 90),
	('James', 99),
	('AD', 96);
select * from students_rank;
# 语法错误
# select id, name,score , rank()   from students_rank;
# 使用三种不同的方法排序
# 排序 常规写法:
select id, name,score from students_rank order by score desc ;
# 结果如下:
# +--+-----+-----
# |id|name |score
# +--+-----+-----
# |1 |curry|100
# |3 |KD   |100
# |2 |klay |99
# |5 |James|99
# |6 |AD   |96
# |4 |green|90
# +--+-----+-----
select id, name,score, rank() over(order by score desc) as r from students_rank;
# 结果如下:
# +--+-----+-----+-+
# |id|name |score|r|
# +--+-----+-----+-+
# |1 |curry|100  |1|
# |3 |KD   |100  |1|
# |2 |klay |99   |3|
# |5 |James|99   |3|
# |6 |AD   |96   |5|
# |4 |green|90   |6|
# +--+-----+-----+-+
# DENSE :名次之间是连续的整数值 ,名词之间不能有断层
select id, name, score,DENSE_RANK() OVER(order by score desc) as dense_r from students_rank;
#结果如下:
# +--+-----+-----+-------+
# |id|name |score|dense_r|
# +--+-----+-----+-------+
# |1 |curry|100  |1      |
# |3 |KD   |100  |1      |
# |2 |klay |99   |2      |
# |5 |James|99   |2      |
# |6 |AD   |96   |3      |
# |4 |green|90   |4      |
# +--+-----+-----+-------+

select id, name,score, row_number() OVER(order by score desc) as row_r from students_rank;
# 结果如下
# +--+-----+-----+-----+
# |id|name |score|row_r|
# +--+-----+-----+-----+
# |1 |curry|100  |1    |
# |3 |KD   |100  |2    |
# |2 |klay |99   |3    |
# |5 |James|99   |4    |
# |6 |AD   |96   |5    |
# |4 |green|90   |6    |
# +--+-----+-----+-----+

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值