前言

使用 SQL 进行业务数据计算时,经常会遇到两个概念:时间范围时间粒度 。以 最近一天的每小时的用户访问人数 为例:

  • 最近一天 是时间范围
  • 每小时 是时间粒度

常见的时间范围:最近五分钟、最近一小时、最近一天、最近一周、最近一月、最近一年、截止到今天、截止到本周、截止到本月、截止到今年。

常见的时间粒度:五分钟、小时、天、周、月、年。

大多数情况下,我们需要根据计算时间和时间范围,计算出业务数据的开始时间和结束时间,用于过滤业务数据;然后再根据业务数据的业务时间和时间粒度,计算出业务时间点,用于分组统计业务数据。

假设用户访问表(user_visit)记录如下:

id uid timestamp
1 u1 2022-09-19 15:10:58
2 u2 2022-09-19 16:24:19
3 u1 2022-09-20 01:04:03
4 u2 2022-09-20 02:12:36
5 u1 2022-09-20 02:35:03
6 u1 2022-09-20 03:10:27

使用 最近一天 过滤数据,开始时间:2022-09-20 00:00:00,结束时间:2022-09-21 00:00:00,SQL 伪代码:

SELECT
*
FROM
user_visit
WHERE
timestamp >= "2022-09-20 00:00:00"
AND timestamp < "2022-09-21 00:00:00"

过滤结果:

id uid timestamp
3 u1 2022-09-20 01:04:03
4 u2 2022-09-20 02:12:36
5 u1 2022-09-20 02:35:03
6 u1 2022-09-20 03:10:27

过滤后的业务数据,使用 小时 将业务时间转换成业务时间点,转换结果:

id uid timestamp
3 u1 2022-09-20 01:00:00
4 u2 2022-09-20 02:00:00
5 u1 2022-09-20 02:00:00
6 u1 2022-09-20 03:00:00

按小时分组统计用户访问人数,SQL 伪代码:

SELECT
timestamp, COUNT(DISTINCT(uid)) AS uids
FROM
user_visit
GROUP BY
timestamp

统计结果:

timestamp uids
2022-09-20 01:00:00 1
2022-09-20 02:00:00 2
2022-09-20 03:00:00 1

整个过程涉及两个关键的时间计算:

  • 根据计算时间和时间范围,计算业务数据开始时间和结束时间
  • 根据业务时间和时间粒度,计算业务时间点

这两个时间的计算均需要通过 SQL 的 日期时间函数 实现。然而不同的数据库对于日期时间函数的支持程度差异很大,实际的计算过程可能比较繁琐。

本文以阿里云 ODPS 和 RDS 为例,详细说明日期时间函数关于时间范围和时间粒度的计算方法。

时间范围的开始时间是闭区间,结束时间是开区间。

时间类型

阿里云的 ODPS 和 RDS 都是支持日期时间(DATETIME)类型的,业务数据可以直接使用 DATETIME 存储业务时间;也可以使用其它数据类型存储业务时间,常见的有日期时间字符串(STRING)和 Unix 时间戳(INT)。

我们建议将业务时间统一转换成 DATETIME 类型之后再进行时间计算。

日期时间字符串

以字符串 2022-09-20 15:10:58 例,将其转换成 DATETIME。

ODPS

TO_DATE('2022-09-20 15:10:58', 'yyyy-mm-dd hh:mi:ss')

RDS

STR_TO_DATE('2022-09-20 15:10:58', '%Y-%m-%d %H:%i:%s')

Unix 时间戳

以时间戳 1663657859 为例,将其转换成 DATETIME。

ODPS

FROM_UNIXTIME(1663657859)

RDS

FROM_UNIXTIME(1663657859)

时间范围

我们使用 当前时间 指代 计算时间,获取当前时间(DATETIME):

ODPS

GETDATE()

RDS

NOW()

最近五分钟

以计算时间:2022-09-20 17:07:33 为例,最近五分钟的业务开始时间应为:2022-09-20 17:00:00,业务结束时间应为:2022-09-20 17:05:00。

ODPS

// 开始时间
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(GETDATE()) / 300 - 1) * 300) // 结束时间
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(GETDATE()) / 300) * 300)

RDS

// 开始时间
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW()) / 300 - 1) * 300) // 结束时间
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW()) / 300) * 300)

300 表示 5 分钟,即:300 秒。

最近一小时

以计算时间 2022-09-20 17:19:57 为例,最近一小时的业务开始时间应为 2022-09-20 16:00:00,业务结束时间应为 2022-09-20 17:00:00。

ODPS

// 开始时间
DATETRUNC(DATEADD(GETDATE(), -1, 'hh'), 'hh') // 结束时间
DATETRUNC(GETDATE(), 'hh')

RDS

// 开始时间
DATE_FORMAT(DATE_ADD(NOW(), INTERVAL - 1 HOUR), '%Y-%m-%d %H:00:00') // 结束时间
DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00')

最近一天

以计算时间 2022-09-20 17:31:06 为例,最近一天的业务开始时间应为 2022-09-19 00:00:00,业务结束时间应为 2022-09-20 00:00:00。

ODPS

// 开始时间
DATETRUNC(DATEADD(GETDATE(), -1, 'dd'), 'dd') // 结束时间
DATETRUNC(GETDATE(), 'dd')

RDS

// 开始时间
DATE_FORMAT(DATE_ADD(NOW(), INTERVAL - 1 DAY), '%Y-%m-%d 00:00:00') // 结束时间
DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')

最近一周

以计算时间 2022-09-20 17:48:10 为例,最近一周的业务开始时间应为 2022-09-12 00:00:00,业务结束时间应为 2022-09-19 00:00:00。

ODPS

// 开始时间
DATETRUNC(DATEADD(GETDATE(), - WEEKDAY(GETDATE()) - 7 , 'dd'), 'dd') // 结束时间
DATETRUNC(DATEADD(GETDATE(), - WEEKDAY(GETDATE()), 'dd'), 'dd')

RDS

// 开始时间
DATE_FORMAT(ADDDATE(NOW(), - 7 - WEEKDAY(NOW())), '%Y-%m-%d 00:00:00') // 结束时间
DATE_FORMAT(ADDDATE(NOW(), - WEEKDAY(NOW())), '%Y-%m-%d 00:00:00')

最近一月

以计算时间 2022-09-20 17:57:05 为例,最近一月的业务开始时间应为 2022-08-01 00:00:00,业务结束时间应为 2022-09-01 00:00:00。

ODPS

// 开始时间
DATETRUNC(DATEADD(GETDATE(), -1, 'mm'), 'mm') // 结束时间
DATETRUNC(GETDATE(), 'mm')

RDS

// 开始时间
DATE_FORMAT(DATE_ADD(NOW(), INTERVAL - 1 MONTH), '%Y-%m-01 00:00:00') // 结束时间
DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00')

最近一年

以计算时间 2022-09-20 18:03:00 为例,最近一年的业务开始时间应为 2021-01-01 00:00:00,业务结束时间应为 2022-01-01 00:00:00。

ODPS

// 开始时间
DATETRUNC(DATEADD(GETDATE(), -1, 'yyyy'), 'yyyy') // 结束时间
DATETRUNC(GETDATE(), 'yyyy')

RDS

// 开始时间
DATE_FORMAT(DATE_ADD(NOW(), INTERVAL - 1 YEAR), '%Y-01-01 00:00:00') // 结束时间
DATE_FORMAT(NOW(), '%Y-01-01 00:00:00')

截止到今天

以计算时间 2022-09-20 18:12:31 为例,截止到今天的业务开始时间应为 2022-09-20 00:00:00,业务结束时间应为 2022-09-21 00:00:00。

ODPS

// 开始时间
DATETRUNC(GETDATE(), 'dd') // 结束时间
DATETRUNC(DATEADD(GETDATE(), 1, 'dd'), 'dd')

RDS

// 开始时间
DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00') // 结束时间
DATE_FORMAT(ADDDATE(NOW(), 1), '%Y-%m-%d 00:00:00')

截止到本周

以计算时间 2022-09-20 18:16:20 为例,截止到本周的业务开始时间应为 2022-09-19 00:00:00,业务结束时间应为 2022-09-26 00:00:00。

ODPS

// 开始时间
DATETRUNC(DATEADD(GETDATE(), - WEEKDAY(GETDATE()), 'dd'), 'dd') // 结束时间
DATETRUNC(DATEADD(GETDATE(), 7 - WEEKDAY(GETDATE()), 'dd'), 'dd')

RDS

// 开始时间
DATE_FORMAT(ADDDATE(NOW(), - WEEKDAY(NOW())), '%Y-%m-%d 00:00:00') // 结束时间
DATE_FORMAT(ADDDATE(NOW(), 7 - WEEKDAY(NOW())), '%Y-%m-%d 00:00:00')

截止到本月

以计算时间 2022-09-20 18:19:15 为例,截止到本月的业务开始时间为 2022-09-01 00:00:00,业务结束时间应为 2022-10-01 00:00:00。

ODPS

// 开始时间
DATETRUNC(GETDATE(), 'mm') // 结束时间
DATETRUNC(DATEADD(GETDATE(), 1, 'mm'), 'mm')

RDS

// 开始时间
DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00') // 结束时间
DATE_FORMAT(ADDDATE(NOW(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')

截止到今年

以计算时间 2022-09-20 18:21:09 为例,截止到今年的业务开始时间为 2022-01-01 00:00:00,业务结束时间应为 2023-01-01 00:00:00。

ODPS

// 开始时间
DATETRUNC(GETDATE(), 'yyyy') // 结束时间
DATETRUNC(DATEADD(GETDATE(), 1, 'yyyy'), 'yyyy')

RDS

// 开始时间
DATE_FORMAT(NOW(), '%Y-01-01 00:00:00') // 结束时间
DATE_FORMAT(ADDDATE(NOW(), INTERVAL 1 YEAR), '%Y-01-01 00:00:00')

时间粒度

五分钟

参考时间范围为最近五分钟的结束时间的计算方法。

小时

参考时间范围为最近一小时的结束时间的计算方法。

参考时间范围为最近一天的结束时间的计算方法。

参考时间范围为最近一周的结束时间的计算方法。

参考时间范围为最近一月的结束时间的计算方法。

参考时间范围为最近一年的结束时间的计算方法。

结语

时间范围和时间粒度的计算虽然不是什么技术难点,却是数据分析 SQL 语句中极其重要的组成部分。不同数据库之间的日期时间函数的支持程度差异较大,具体使用时很容易混淆,如果平时可以多记录多总结,则可以幅度提升开发效率。

SQL 时间范围和时间粒度的更多相关文章

  1. SQL Server时间粒度系列----第3节旬、月时间粒度详解

    本文目录列表: 1.SQL Server旬时间粒度2.SQL Server月有关时间粒度 3.SQL Server函数重构 4.总结语 5.参考清单列表   SQL Server旬时间粒度       ...

  2. SQL Server时间粒度系列----第4节季、年时间粒度详解

    本文目录列表: 1.SQL Server季时间粒度2.SQL Server年时间粒度 3.总结语 4.参考清单列表   SQL Serve季时间粒度       季时间粒度也即是季度时间粒度.一年每3 ...

  3. SQL Server时间粒度系列----第2节日期、周时间粒度详解

    本文目录列表: 1.从MySQL提供的TO_DAYS和FROM_DAYS这对函数说起2.SQL Server日期时间粒度3.SQL Server周有关时间粒度 4.总结语 5.参考清单列表   从My ...

  4. SQL Server时间粒度系列----第5节小时、分钟时间粒度详解

    本文目录列表: 1.SQL Server小时时间粒度2.SQL Server分钟时间粒度 3.总结语 4.参考清单列表   SQL Server小时时间粒度          这里说的时间粒度是指带有 ...

  5. SQL Server时间粒度系列

        工作中经常遇到针对业务部门提出不同时间粒度(年.季度.月.周.日等等日期时间粒度,以下简称时间粒度)的数据统计汇总任务,也看到不少博友针对这方便的博文,结合SQL Server的日期时间函数和 ...

  6. SQL Server时间粒度系列----第9节时间粒度示例演示

    本文目录列表: 1.准备测试数据 2.向测试数据表添加相关时间粒度字段列 3.基于日月季年统计汇总的演示 4.总结语 5.参考清单列表   准备测试数据   为了提供不同时间粒度示例的演示,就需要测试 ...

  7. SQL Server时间粒度系列----第7节日历数据表详解

    本文目录列表: 1.时间粒度有关描述 2.时间维度有关功能函数3.日历数据表 4.日历数据表数据填充 5.总结语 6.参考清单列表   时间粒度有关描述   将该系列涉及到的时间粒度以及分钟以下的粒度 ...

  8. SQL Server时间粒度系列----第1节时间粒度概述

    本文目录列表: 1.什么是时间粒度?2.SQL Server提供的时间粒度3.SQL Server时间粒度代码演示   4.SQL Server基准日期 5.总结语6.参考清单列表   什么是时间粒度 ...

  9. sql server日期时间转字符串

    一.sql server日期时间函数Sql Server中的日期与时间函数 1.  当前系统日期.时间     select getdate()  2. dateadd  在向指定日期加上一段时间的基 ...

随机推荐

  1. 如何借助Chrome Tool Dev 帮助前端开发

    11111111111111111111111111111111111111111111111111

  2. 浏览器中的原生base64方法

    在web开发中,经常涉及到对文本.文件等进行base64编码处理,在之前的开发中,使用js-base64来进行处理,但其实浏览器已经原生包含了处理方法.性能更好,兼容性也更高. atob() - AS ...

  3. java------注释、关键字、字面量

    注释(对代码的一种解释说明) 单行注释   // 多行注释   /*   */ 文档注释 /** */ 注释使用细节: 注释内容不参与编译和运行,所以只在java文件中存在 不管是单行注释还是多行注释 ...

  4. 转换流的原理和OutputStreamWriter介绍&代码实现

    转换流的原理 OutputStreamWriter介绍&代码实现 package com.yang.Test.ReverseStream; import java.io.FileNotFoun ...

  5. python代码如何写的优雅?

    简介 在实际项目中,我们可能一开始为了完成功能而忽视了代码的整体质量,因此,使用一些高阶的函数或方法,能够更加使我们的代码更加优雅.废话不多说,现在马上开始. 使用enumerate方法替代range ...

  6. linux学习之selinux安全处理

    linux在默认情况下会开启selinux,如果软件开放的端口不是selinux默认的协议端口会导致防火墙端口开放后还是无法访问.以下是解决办法. [root@localhost ~]# semana ...

  7. Vue el与data的两种写法 && Object.defineProperty方法

    1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 & ...

  8. [spring]spring详细总结

    spring 1.spring简介 Spring框架是一个开源的应用程序框架,是针对bean的生命周期进行管理的轻量级容器. Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强 ...

  9. WPF 截图控件之移除控件(九)「仿微信」

    WPF 截图控件之移除控件(九)「仿微信」 WPF 截图控件之移除控件(九)「仿微信」 作者:WPFDevelopersOrg 原文链接: https://github.com/WPFDevelope ...

  10. 8. 利用Ansible快速构建MGR | 深入浅出MGR

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 目录 1. 安装ansbile 2. 配置ansible 3. 建立ssh信任 4. 测试ansible 5. 使用ans ...