MySQL连续出现的数字

Posted willem_chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL连续出现的数字相关的知识,希望对你有一定的参考价值。

SQL架构

Create table If Not Exists Logs (Id int, Num int);
Truncate table Logs
insert into Logs (Id, Num) values ('1', '1');
insert into Logs (Id, Num) values ('2', '1');
insert into Logs (Id, Num) values ('3', '1');
insert into Logs (Id, Num) values ('4', '2');
insert into Logs (Id, Num) values ('5', '1');
insert into Logs (Id, Num) values ('6', '2');
insert into Logs (Id, Num) values ('7', '2');

题目描述

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| num         | varchar |
+-------------+---------+
id 是这个表的主键。

编写一个 SQL 查询,查找所有至少连续出现三次的数字。

返回的结果表中的数据可以按 任意顺序 排列。

查询结果格式如下面的例子所示:

Logs 表:
+----+-----+
| Id | Num |
+----+-----+
| 1  | 1   |
| 2  | 1   |
| 3  | 1   |
| 4  | 2   |
| 5  | 1   |
| 6  | 2   |
| 7  | 2   |
+----+-----+

Result 表:
+-----------------+
| ConsecutiveNums |
+-----------------+
| 1               |
+-----------------+
1 是唯一连续出现至少三次的数字。

题解

方法:用 DISTINCTWHERE 语句

算法

连续出现的意味着相同数字的 Id 是连着的,由于这题问的是至少连续出现 3 次,我们使用 Logs 并检查是否有 3 个连续的相同数字。

SELECT *
FROM
    Logs l1,
    Logs l2,
    Logs l3
WHERE
    l1.Id = l2.Id - 1
    AND l2.Id = l3.Id - 1
    AND l1.Num = l2.Num
    AND l2.Num = l3.Num;

+------+------+------+------+------+------+
| Id   | Num  | Id   | Num  | Id   | Num  |
+------+------+------+------+------+------+
|    1 |    1 |    2 |    1 |    3 |    1 |
+------+------+------+------+------+------+
1 row in set (0.01 sec)

注意:前两列来自 l1 ,接下来两列来自 l2 ,最后两列来自 l3 。

然后我们从上表中选择任意的 Num 获得想要的答案。同时我们需要添加关键字 DISTINCT ,因为如果一个数字连续出现超过 3 次,会返回重复元素。

SELECT DISTINCT
    l1.Num AS ConsecutiveNums
FROM
    Logs l1,
    Logs l2,
    Logs l3
WHERE
    l1.Id = l2.Id - 1
    AND l2.Id = l3.Id - 1
    AND l1.Num = l2.Num
    AND l2.Num = l3.Num;
    
+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

答1

解题思路

连结

将logs起3个别名,然后t1.id=t2.id+1 t2.id=t3.id+1,3个表的num也要一样

SELECT
	DISTINCT t2.num AS ConsecutiveNums 
FROM
	Logs t1,
	Logs t2,
	Logs t3 
WHERE
	t1.id = t2.id + 1 
	AND t2.id = t3.id + 1 
	AND t1.num = t2.num 
	AND t2.num = t3.num;
+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

答2

SELECT DISTINCT a.Num "ConsecutiveNums"
FROM Logs a
    INNER JOIN Logs b ON a.id + 1 = b.id
    INNER JOIN Logs c ON b.id + 1 = c.id
WHERE a.Num = b.Num AND b.Num = c.Num;

+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

答3

①首先遍历一遍整张表,找出每个数字的连续重复次数

具体方法为:

初始化两个变量,一个为pre,记录上一个数字;一个为count,记录上一个数字已经连续出现的次数。

然后调用 if() 函数,如果 pre 和当前行数字相同,count 加 1 极为连续出现的次数;如果不同,意味着重新开始一个数字,count重新从1开始。

最后,将当前的Num数字赋值给pre,开始下一行扫描。

select 
	Num,    #当前的Num 数字
	if(@pre=Num,@count := @count+1,@count := 1) as nums, #判断 和 计数
	@pre:=Num as mer   #将当前Num赋值给pre
from Logs;
+------+------+------+
| Num  | nums | mer  |
+------+------+------+
|    1 |    1 |    1 |
|    1 |    2 |    1 |
|    1 |    3 |    1 |
|    2 |    1 |    2 |
|    1 |    1 |    1 |
|    2 |    1 |    2 |
|    2 |    2 |    2 |
+------+------+------+
7 rows in set (0.00 sec)

上面这段代码执行结果就是一张三列为Num,count as nums,pre的表。

②将上面表的结果中,重复次数大于等于3的数字选出,再去重即为连续至少出现三次的数字。

select 
	distinct Num as ConsecutiveNums 
from  
	(select Num,
		if(@pre=Num,@count := @count+1,@count := 1) as nums,
			@pre:=Num
			from Logs as l ,
			(select @pre:= null,@count:=1) as pc
	) as n
where nums >=3;

+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

注意:pre初始值最好不要赋值为一个数字,因为不确定赋值的数字是否会出现在测试表中。

答4

SELECT DISTINCT Num AS ConsecutiveNums 
FROM (
SELECT Num, 
CASE WHEN Num = @pre_num THEN @count:=@count+1
     ELSE @count:=1 END AS continue_count,
@pre_num:=Num AS pre_num
FROM `Logs`, (SELECT @pre_num:=NULL, @count:=1) AS init
) AS temp 
WHERE continue_count = 3;

+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

知识点

MySql 中关键字 case when then else end 的用法

SELECT
    case                   -------------如果
    when sex='1' then '男' -------------sex='1',则返回值'男'
    when sex='2' then '女' -------------sex='2',则返回值'女'
    else '其他'                 -------------其他的返回'其他’
    end                    -------------结束
from   sys_user

整体理解: 在sys_user表中如果sex=‘1’,则返回值’男’如果sex=‘2’,则返回值’女’ 否则返回’其他’

case具有两种格式,简单case函数和case搜索函数

--简单case函数
case sex
  when '1' then '男'
  when '2' then '女’
  else '其他' end
  
--case搜索函数
case when sex = '1' then '男'
     when sex = '2' then '女'
     else '其他' end  

这两种方式,可以实现相同的功能。简单case函数的写法相对比较简洁,但是和case搜索函数相比,功能方面会有些限制,比如写判定式。

还有一个需要注重的问题,case函数只返回第一个符合条件的值,剩下的case部分将会被自动忽略。

--比如说,下面这段sql,你永远无法得到“第二类”这个结果
case when col_1 in ('a','b') then '第一类'
     when col_1 in ('a') then '第二类'
     else '其他' end  

SQL架构

CREATE TABLE `n_users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `sex` decimal(10,0) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('1', '张一', '0');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('2', '张二', '1');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('3', '张三', '0');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('4', '张四', '0');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('5', '张五', '2');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('6', '张六', '1');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('7', '张七', '2');
INSERT INTO `mysqldemo`.`n_users` (`id`, `name`, `sex`) VALUES ('8', '张八', '1');

1、表中的"sex"是用数字表示的,希望用中文显示。可以使用case语句:

select u.id,u.name,u.sex,
	(case u.sex
		when 1 then '男'
		when 2 then '女'
			else '其他'
			end
		)性别
	from n_users u;

+----+--------+-----+--------+
| id | name   | sex | 性别   |
+----+--------+-----+--------+
|  1 | 张一   |   0 | 其他   |
|  2 | 张二   |   1 ||
|  3 | 张三   |   0 | 其他   |
|  4 | 张四   |   0 | 其他   |
|  5 | 张五   |   2 ||
|  6 | 张六   |   1 ||
|  7 | 张七   |   2 ||
|  8 | 张八   |   1 ||
+----+--------+-----+--------+
8 rows in set (0.00 sec)

2、如果不希望列表中出现"sex"列,语句如下:

select u.id,u.name,
	(case u.sex
		when 1 then '男'
		when 2 then '女'
			else '其他'
			end
		)性别
	from n_users u;
+----+--------+--------+
| id | name   | 性别   |
+----+--------+--------+
|  1 | 张一   | 其他   |
|  2 | 张二   ||
|  3 | 张三   | 其他   |
|  4 | 张四   | 其他   |
|  5 | 张五   ||
|  6 | 张六   ||
|  7 | 张七   ||
|  8 | 张八   ||
+----+--------+--------+
8 rows in set (0.00 sec)

3、将sum与case结合使用,可以实现分段统计。

如果现在希望将上表中各种性别的人数进行统计,sql语句如下:

select
	sum(case u.sex when 1 then 1 else 0 end)男性,
	sum(case u.sex when 2 then 1 else 0 end)女性,
	sum(case when u.sex <>1 and u.sex<>2 then 1 else 0 end)性别为空
from n_users as u;

+--------+--------+--------------+
| 男性   | 女性   | 性别为空     |
+--------+--------+--------------+
|      3 |      2 |            3 |
+--------+--------+--------------+
1 row in set (0.00 sec)
select
	count(case when u.sex=1 then 1 end)男性,
	count(case when u.sex=2 then 1 end),
	count(case when u.sex <>1 and u.sex<>2 then 1 end)性别为空
from n_users u;

+--------+-----+--------------+
| 男性   || 性别为空     |
+--------+-----+--------------+
|      3 |   2 |            3 |
+--------+-----+--------------+
1 row in set (0.00 sec)

MySQL-IF()函数

定义

IF函数根据条件的结果为true或false,返回第一个值,或第二个值

语法
IF(condition, value_if_true, value_if_false)

参数

参数描述
condition必须,判断条件
value_if_true可选,当条件为true值返回的值
condition可选,当条件为false值返回的值

例子

SELECT IF(500<1000, 5, 10);
SELECT IF(STRCMP("hello","bye") = 0, "YES", "NO");

mysql> select *,if(sex=1,"男","女") as '性别' from n_users;
+----+--------+-----+--------+
| id | name   | sex | 性别   |
+----+--------+-----+--------+
|  1 | 张一   |   0 ||
|  2 | 张二   |   1 ||
|  3 | 张三   |   0 ||
|  4 | 张四   |   0 ||
|  5 | 张五   |   2 ||
|  6 | 张六   |   1 ||
|  7 | 张七   |   2 |以上是关于MySQL连续出现的数字的主要内容,如果未能解决你的问题,请参考以下文章

180.Mysql之连续出现的数字

如何计算bash中一列数据中的连续重复次数?

SQL:连续天数的计算方法

连接MySQL出现错误:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)(代码片段

尺取法

在列表中找到相同数字的最大连续出现而不导入任何模块