前言

使用 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*
FROMuser_visit
WHEREtimestamp >= "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 伪代码:

SELECTtimestamp, COUNT(DISTINCT(uid)) AS uids
FROMuser_visit
GROUP BYtimestamp

统计结果:

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时间粒度系列----第4节季、年时间粒度详解

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

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

    从MySQL提供的TO_DAYS和FROM_DAYS针对函数说起 学习和使用过MySQL的博友,大都知道MySQL提供了很多针对日期和时间的函数,提供了获取不同时间粒度上的功能.相对而然SQL Ser ...

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

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

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

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

  5. sql中计算两个日期之间相差的时间粒度—TIMESTAMPDIFF函数

    TIMESTAMPDIFF(时间粒度,开始时间,结束时间):计算开始时间和结束时间相差的(天数.小时数.分钟数.秒数等等),结果会取整** 时间粒度参数有: 1. year:计算开始时间和结束时间相差 ...

  6. js手机端时间区间_js根据时间范围生成时间刻度数据

    前言: 使用d3绘制时间轴图表,不可避免的涉及到动态变动时间刻度,根据业务需求需要调整查看不同的时间粒度数据.如果后台数据非连续数据,需要前端自己处理的话,就得自己根据时间范围创建时间刻度数据. 一. ...

  7. R把天数据按照不同时间粒度聚合数据(Aggregate)

    R把天数据按照不同时间粒度聚合数据(Aggregate) 目录 R把天数据按照不同时间粒度聚合数据(Aggregate) 聚合天数据

  8. Sql server中时间函数用法详解

    SQL中的时间函数非常有用,特别是在我们进行初始赋值.复杂查询的时候,就显得特别方便. 1.获得系统当前时间 select getdate()  2.DateName ( datepart , dat ...

  9. SQL SERVER查询时间条件式写法

    如下表: tid    tname     tdate     1      张三      2009-10-29 19:04:50     2      李四      2006-11-12 11: ...

最新文章

  1. 减少HQL 的MAP数
  2. 实验室电子设备选型-2020-年末
  3. bigdecimal正确用法_深入理解 BigDecimal 的使用
  4. Mysqli面向对象操作数据库
  5. [js] axios为什么可以使用对象和函数两种方式调用?是如何实现的?
  6. Redis:复制,第3部分——redis-py和Python中的Redi哨兵一起使用
  7. protobuf 安装_Beego 安装时 一直无响应(443)怎么办
  8. (转)Inno Setup入门(七)——提供安装语言选项
  9. 前端面试instanceof_【面试准备】每日前端面试题 45 (前端校招字节跳动面试4)...
  10. java square类_java – 处理大类
  11. .Net向Page和UpdatePanel输出JS
  12. freeradius安装
  13. tesseract_OCR识别英文
  14. 几个简单规则改进你的SEO效果
  15. 【期望】几何分布的期望
  16. 每日一题#10-22 Ransomware
  17. CSS3配合JavaScript图片爆炸效果
  18. 十年弄潮 ——从《才富》到《中国人力知本》
  19. PySpark reduce reduceByKey用法
  20. 关于2022年国内软件质量调查问卷的一些感悟与收获

热门文章

  1. pg_waldump工具
  2. PLC中ST编程的起保停
  3. 联易融多维度构建安全科技体系能力,为客户数据隐私保驾护航
  4. iOS开发中图片的一些处理操作(背景色,透明度,合成,大小)
  5. 3年自动化测试,你真的理解自动化测试持续集成(CI)吗?
  6. 交集和并集傻傻分不清楚(解决办法)
  7. html中样式表的类型,css层叠样式表有哪几种类型?
  8. XCZU15EG学习资料保存第527篇:基于3U VPX XCZU15EG+TMS320C6678的信号处理板
  9. OCI学习-Storage:Block Volume
  10. PCL下使用区域增长算法进行点云平面分割的实现