题目要求
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
+----+-----+
| Id | Num |
+----+-----+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
+----+-----+
例如,给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。
+-----------------+
| ConsecutiveNums |
+-----------------+
| 1 |
+-----------------+
审题
出现三次是很好实现的, order by num 然后count即可。
但是连续的怎么实现呢?
将表格前置一项,再后置一项进行联结。如果一行出现的三个数相等就至少出现三次了。(后置一项,再后置两项也可以)
构造数据
CREATE TABLE `Logs` (ID INT, num INT) ;
INSERT INTO `Logs` VALUE(1,1),(2,1),(3,1),(4,2),(5,1),(6,2),(7,2);
SELECT * FROM `Logs`;
自己的解答
主表与后置一项的表合并
SELECT
L1.ID AS ID1,
L1.`num` AS num1,
L2.`ID` AS ID2,
L2.`num` AS num2
FROM
`Logs` L1
LEFT JOIN `Logs` L2
ON L1.`ID` = L2.`ID` + 1 ;
注:用别名的重要性 直接select * 列名没法区分

再与前置一项的进行合并
SELECT tmp.*, L3.`ID` AS ID3, L3.`num` AS num3
FROM (SELECT
L1.ID AS ID1,
L1.`num` AS num1,
L2.`ID` AS ID2,
L2.`num` AS num2
FROM
`Logs` L1
LEFT JOIN `Logs` L2
ON L1.`ID` = L2.`ID` + 1) tmp
LEFT JOIN `Logs` L3
ON tmp.Id1 = L3.`ID` - 1;

num1为主项, num2为后置一项, num3为前置一项。
然后判断num1, num2,num3相等即可。
SELECT tmp.num1 AS ConsecutiveNums
FROM (SELECT
L1.ID AS ID1,
L1.`num` AS num1,
L2.`ID` AS ID2,
L2.`num` AS num2
FROM
`Logs` L1
LEFT JOIN `Logs` L2
ON L1.`ID` = L2.`ID` + 1) tmp
LEFT JOIN `Logs` L3
ON tmp.Id1 = L3.`ID` - 1
WHERE tmp.num1 = num2 AND tmp.num1 = L3.`num`;
额 又没通过

原因是: 如果出现四个连续的3 这时候会选出来两个3 因此最终结果应该去重。

通过了但不是很快。 而且很局限吧,如果想找出连续出现6次的数据难道要联结6个表么。
缺点:
1、效率较低,
2、如果找连续出现n次不方便
3、id不连续不行,但可以改用rownumber或其他方法获取相邻记录
其他解法
1、官方的解法
其实和我的方法类似 区别在于联结方式 sql92与sql99的区别
还是按照我的思路写一下
SELECT *
FROM
LOGS l1,
LOGS l2,
LOGS l3
WHERE
l1.Id +1 = l2.Id
AND l2.Id = l3.Id - 1

SELECT DISTINCT
l2.Num AS ConsecutiveNums
FROM
LOGS l1,
LOGS l2,
LOGS l3
WHERE
l1.Id +1 = l2.Id
AND l2.Id = l3.Id - 1
AND l1.Num = l2.Num
AND l2.Num = l3.Num
;
其实也可以联结后置1的序列,再联结后置2的序列。这样就可以实现了,也就是官方答案。
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
;
2、定义变量来解决
- 抛开id。仅从行数据考虑,需要用户变量记录前一行数据。当前行数据与前一行数据比较是否相同。
定义两个用户变量:
@pre : 前一行数据
@dcount :与前一行数据连续相同的个数 - 初始化@pre和@dcount,定义为一张表:(SELECT @pre:= NULL,@dcount:=0) AS tmp
计算出与每行数字连续相同的数字个数。
与前一行数据比较的逻辑为:
@dcount:= IF(@pre=L.Num, @dcount+1, 1)
@pre与L.Num : 前一行数据与当前行相同。
如果相同,@dcount+1,否则@dcount=1。
这些结果形成一张新表:a(Num,dcount,USELESS)。
最终只要从a中取出cnt大于等于3的行。
SELECT
L.`num`,
IF(
@pre = L.`num`,
@dcount := @dcount + 1,
@dcount := 1
) AS dcounts,
@pre := L.`num`
FROM
`Logs` AS L,
(SELECT
@pre := NULL,
@dcount := 1) AS tmp ;

这个结果就与id无关,dcounts记录的是与前一行数据连续相同的个数。
SELECT DISTINCT
tmp2.num AS ConsecutiveNums
FROM
(SELECT
L.`num`,
IF(
@pre = L.`num`,
@dcount := @dcount + 1,
@dcount := 1
) AS dcounts,
@pre := L.`num`
FROM
`Logs` AS L,
(SELECT
@pre := NULL,
@dcount := 1) AS tmp) AS tmp2
WHERE tmp2.`dcounts` >= 3 ;