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 是唯一连续出现至少三次的数字。
方法:用 DISTINCT
和 WHERE
语句
算法
连续出现的意味着相同数字的 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)
解题思路
连结
将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)
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)
具体方法为:
初始化两个变量,一个为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的表。
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初始值最好不要赋值为一个数字,因为不确定赋值的数字是否会出现在测试表中。
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)
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 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
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');
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)
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)
如果现在希望将上表中各种性别的人数进行统计,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)
定义
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 | 女 | | 8 | 张八 | 1 | 男 | +----+--------+-----+--------+ 8 rows in set (0.00 sec)
SELECT USER_CODE AS '用户编码', IF(USER_STATE = 1, '正常', '冻结') AS '用户状态', ( CASE USER_TYPE WHEN 1 THEN '用户' WHEN 2 THEN '商家' WHEN 3 THEN '自营' ELSE '未知' END ) AS '用户类型' FROM USER_INFO LIMIT 10;