https://www.nowcoder.com/feed/main/detail/cf3c154d344a4d858ffca4230ad2f391

题目描述

这是一道非常经典的问题,连续n天登录的用户。之前在面试的时候也有被问到过,但是当时答的不好,这里在leetcode里再次碰到,所以做个总结,leetcode中问的是连续5天,但是我们希望将其拓展到连续n天。

以下是题目中的原始表

第一张是用户表,id列为主键。

第二张表为登录信息,因为其中包含重复行,表示一个用户在同一天多次登录,所以这张表无主键。

思路

说实话,第一次遇到这个问题,我是懵逼的,唯一想到的方法就是创建多张子表,每张表的日期依次向前推一天,然后多表连接。但是这种方法显然低效。同时,并不能解决任意天数n的情况,比如连续100天,总不能去创建100张子表吧?

OK,那么还有什么优雅的解法呢?

这里需要用到一个 row_number()方法,对每个用户的登录日期(去重后)按日期先后进行标号,然后用日期去减去这个标号。如果是一系列连续的日期,标号也是一系列连续递增的数,那么相减(日期减去对应标号)之后的结果就应该是相同的。然后我们对结果去进行count(),如果对于某个用户相同值的count大于等于n,那么就意味这这个用户存在连续n天登录。

以下是具体代码。

SELECT Accounts.name
FROM (
	SELECT DISTINCT id, res
	FROM (
		SELECT id, date_sub(login_date, INTERVAL cum DAY) AS res
		FROM (
			SELECT *, row_number() OVER (PARTITION BY id ORDER BY login_date) AS cum
			FROM (
				SELECT DISTINCT login_date, id
				FROM Logins
			) t0
		) t1
	) t2
	GROUP BY id, res
	HAVING COUNT(*) >= 5
) t3
	LEFT JOIN Accounts ON t3.id = Accounts.id;

稍微做下说明:

t1表得到的结果就是对每个用户进行按日期标号