n卡录双屏出现问题_mysql:连续出现N次类问题

本文介绍了如何使用SQL查询数据中连续出现三次的成绩,并提供了三种不同的解决方案,包括自连接、窗口函数和变量方法。此外,还展示了如何统计用户连续登录三天及以上的情况,利用窗口函数进行时间连续性的判断。内容涵盖了SQL查询技巧和连续性统计的应用。
摘要由CSDN通过智能技术生成

316960366760356f05fdfed5cc8d59ce.png

一、问题

下面是某班级学生的某课程的成绩表(table name :sscore,col_num: stu_num(学号),score:成绩),使用sql查找所有至少连续出现3次的成绩:

a150f074e2e4f9a83f50ecdd023e592e.png

二、准备

建表

 CREATE TABLE Sscore (
    stu_num VARCHAR(10),
    score DECIMAL(18 , 1 )
);

插入数据

insert into Sscore values('01' , 80);
insert into Sscore values('02' , 70);
insert into Sscore values('03' , 78);
insert into Sscore values('04' , 78);
insert into Sscore values('05' , 78);
insert into Sscore values('06' , 90);
insert into Sscore values('07' , 60);
insert into Sscore values('08' , 71);
insert into Sscore values('09' , 66);

修改一下

alter table sscore modify stu_num varchar(10) primary key;

解法1:关于连续出现N次类问题,自连接解法;

SELECT DISTINCT
    a.score AS continue_3
FROM
    sscore AS a,
    sscore AS b,
    sscore AS c
WHERE
    a.stu_num = b.stu_num - 1
        AND b.stu_num = c.stu_num - 1
        AND a.score = b.score
        AND b.score = c.score;

[解题思路]

1.怎么理解连续出现3次?

即三位同学的学号为依次递增系列。如04,05,06。

2.连续出现3次说明这3为同学的3个成绩相等。

3.使用三张相同的表自连接可以将判断三列相等的问题转化为同一行3个值相等的问题。

自连接写法:

select *
from sscore as a,
   Sscore as b,
   Sscore as c;

这里如果不指定连接三表的主键,那么它们使用全连接。

确定连接三张表的主键

学号依次递增,所以可以用a.学号 = b.学号 - 1 and b.学号 = c.学号-1。

3位同学成绩相等。a.成绩 = b.成绩 and b.成绩 = c.成绩 。

如下图所示:

0d0708302be0f6ecfb9e816d277d8311.png

解法2:

1.我们需要使用row_number()来对stu_num进行排序,sql语句如下:

SELECT 
	stu_num,
	score,
	row_number() over (ORDER BY  stu_num) AS orde
	FROM sscore;

实现如下:

c878a2d5a026c493a6022b84a248ef17.png

2.我们使用partition by score对score分组(也就是score相同的作为一组),然后再用order by对相同的score组里面的stu_num进行排序,sql语句如下:

SELECT
	stu_num,
	score,
	row_number() over (PARTITION BY score ORDER BY stu_num) AS orde
	FROM sscore;

实现如下:

9c3ef46256cfd8f0649c667c82c55d69.png

3.最终代码

SELECT score,count(*) num_count FROM(
		SELECT stu_num,score,
			row_number() over (ORDER BY stu_num)-row_number() over (PARTITION by score order by stu_num) as orde
			from sscore
	)as w GROUP BY score,orde
    having num_count =3;

实现如下:

0ac6abc1682695c93e27510b7a7c4dc0.png

解法3:利用变量

  1. 首先假设变量pre为前一位同学的成绩,默认值为null,变量cnt为重复出现某个成绩的数目,默认为0。
  2. 取第一行,a.成绩 = 当前行成绩 ----> 判断与pre是否相等,如果相等,则cnt = cnt+1,
  3. 若不相等,则cnt = 1, ----> pre = a.成绩。
  4. 遍历到下一行(即第2行),重复步骤2,步骤2中的取第1行变为取读2行......
  5. 最后判断cnt大于等于3的成绩;并去重。
select distinct  dd.score ConsecutiveNums
from  (
select d.score,
       @n :=if(@pre=score,@n+1,@n:=1) count,
       @pre:=score
from sscore d,
     (select @pre:=null, @n :=1) r ) dd
where dd.count>=3;

实现同上。

第三种方法拆解:

SELECT 
    d.score,
    @n:=IF(@pre = score, @n + 1, @n:=1) count,
    @pre:=score
FROM
    sscore d,
    (SELECT @pre:=NULL, @n:=1) r;

实现如下:

881456161393d96b59240308fd50daae.png

三、例题

再来具有实际意义的例题

问题:统计连续登陆的三天数和以上的用户以及他们的首次登陆和最后登陆时间

0290ae6b89e0cdf664096ac424503607.png

模拟建表:

CREATE TABLE test1 (
    uid VARCHAR(20) PRIMARY KEY,
    dt DATETIME
);

修改一下:

alter table test1 modify dt date not null;
alter table test1 drop primary key;

插入数据:

insert into test1 (uid,dt) values("guid01","2018/2/28");
insert into test1 (uid,dt) values("guid01","2018/3/01");
insert into test1 (uid,dt) values("guid01","2018/3/02");
insert into test1 (uid,dt) values("guid01","2018/3/04");
insert into test1( uid,dt) values("guid01","2018/3/05");
insert into test1 (uid,dt) values("guid01","2018/3/06");
insert into test1 (uid,dt) values("guid01","2018/3/07");
insert into test1 (uid,dt) values("guid02","2018/3/01");
insert into test1 (uid,dt) values("guid02","2018/3/02");
insert into test1 (uid,dt) values("guid02","2018/3/03");
insert into test1 (uid,dt) values("guid02","2018/3/06");

通过窗口函数排个序:

select uid,dt,row_number() over(partition by uid order by dt) r_n from test1;

实现如下:

8c2ae07eb883d586c0ef57a9848edba0.png

第二:

select uid,dt,date_sub(dt,interval r_n day) dis from (
select uid,dt,row_number() over(partition by uid order by dt) r_n from test1) t1;

实现如下:

8340e8e2be0656ae50d7a7171aeb5ff2.png

这里使用函数date_sub()是想实现一个时间的连续,前提是做好第一步的排序,如果“dis”是相同的话,那么对应时间字段“dt”的时间区间就是连续的;

解释一下函数date_sub用法,结合着理解上面:

DATE_SUB() 函数从日期减去指定的时间间隔。

DATE_SUB(date,INTERVAL expr type)

date参数是合法的日期表达式。expr 参数是你希望添加的时间间隔。

type值
MICROSECOND
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR
SECOND_MICROSECOND
MINUTE_MICROSECOND
MINUTE_SECOND
HOUR_MICROSECOND
HOUR_SECOND
HOUR_MINUTE
DAY_MICROSECOND
DAY_SECOND
DAY_MINUTE
DAY_HOUR
YEAR_MONTH

最终代码:

select uid,min(dt) as start_date,max(dt) as end_date,count(1) as counts from(
select uid,dt,date_sub(dt,interval r_n day) dis from (
select uid,dt,row_number() over(partition by uid order by dt) r_n from test1) t1
) t2
group by uid,dis 
having counts>=3;

实现如下:

be997d88eefd1981568c0dd87910b027.png

因为不同的用户上线的时间重复在现实中是必然的,所以根据业务要求筛选出连续三天或以上的用户就得根据实际情况做分组筛选了

本人接触SQL并不是很长,接触的实际业务也不是很多,以上是对sql学习过程中这一类的问题总结,思路呈现以及实现。<抱拳,抱拳,抱拳>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值