Leetcode180.连续出现的数字(中等)

题目要求
编写一个 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 ;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容