现在我们正在进行声明性编程,我们不关心维护任何状态和内存中的计数器。我们希望以在线分析SQL的形式表达这一点。即考虑这些数据:
| LOGIN_TIME | | --------------------- | | 2014-03-18 05:37:13 | | 2014-03-16 08:31:47 | | 2014-03-16 06:11:17 | | 2014-03-16 05:59:33 | | 2014-03-15 11:17:28 | | 2014-03-15 10:00:11 | | 2014-03-15 07:45:27 | | 2014-03-15 07:42:19 | | 2014-03-14 09:38:12 |
|
这没有多大帮助。让我们从时间戳中删除小时。这很简单:
SELECT DISTINCT cast(login_time AS DATE) AS login_date FROM logins WHERE user_id = :user_id
|
输出:
| LOGIN_DATE | | ------------ | | 2014-03-18 | | 2014-03-16 | | 2014-03-15 | | 2014-03-14 |
|
现在,我们已经了解了窗口函数,让我们只为每个日期添加一个简单的行号:
SELECT login_date, row_number() OVER (ORDER BY login_date) FROM login_dates
|
输出:
| LOGIN_DATE | RN | | ------------ | ---- | | 2014-03-18 | 4 | | 2014-03-16 | 3 | | 2014-03-15 | 2 | | 2014-03-14 | 1 |
|
还很容易。现在,如果不是单独选择这些值,我们会减去它们会发生什么?
SELECT login_date - row_number() OVER (ORDER BY login_date) FROM login_dates
|
我们得到这样的结果:
| LOGIN_DATE | RN | GRP | | ------------ | ---- | ------------ | | 2014-03-18 | 4 | 2014-03-14 | | 2014-03-16 | 3 | 2014-03-13 | | 2014-03-15 | 2 | 2014-03-13 | | 2014-03-14 | 1 | 2014-03-13 |
|
哇。有趣。所以,14 - 1 = 13,15 - 2 = 13,16 - 3 = 13,但是18 - 4 = 14.
这个动作代表一个简单的判断:
- ROW_NUMBER()没有间隙。这就是它的定义方式
- 但是,我们的数据确实如此
因此,当我们从“连续”系列的非连续日期中减去“无间隙”系列连续整数时,我们将为连续日期的每个“无间隙”子系列获得相同的日期,我们将再次获得新的有间隙的日期系列。
这意味着我们现在只需GROUP BY这个任意日期值:
SELECT min(login_date), max(login_date), max(login_date) - min(login_date) + 1 AS length FROM login_date_groups GROUP BY grp ORDER BY length DESC
|
我们已经完成了。已找到最大系列的连续日期,没有间隙:
| MIN | MAX | LENGTH | |------------|------------|--------| | 2014-03-14 | 2014-03-16 | 3 | | 2014-03-18 | 2014-03-18 | 1 |
|
完整查询为:
WITH login_dates AS ( SELECT DISTINCT cast(login_time AS DATE) login_date FROM logins WHERE user_id = :user_id ), login_date_groups AS ( SELECT login_date, login_date - row_number() OVER (ORDER BY login_date) AS grp FROM login_dates ) SELECT min(login_date), max(login_date), max(login_date) - min(login_date) + 1 AS length FROM login_date_groups GROUP BY grp ORDER BY length DESC
|
查询本身确实非常简单和优雅。你决不能以比这更精简的方式实现一些命令式算法。