← 返回题目列表

SQL 按日期和时间维度统计有哪些注意点?

中等 第 20 / 28 题 更新于 2026/07/29
SQL日期统计时间范围时区

简化版

按日期统计要注意时间范围、时区、函数导致索引失效、缺失日期补齐和边界闭开区间。常见写法是用 created_at >= start AND created_at < end,而不是 DATE(created_at) = '2026-07-29'。如果要按天、月分组,可以在结果层做日期截断,但过滤条件尽量保持字段不被函数包裹,以便使用索引。

详细版

日期统计看起来简单,但线上最容易出边界问题。一天的范围不是简单字符串比较,跨时区、夏令时、毫秒精度和数据库字段类型都会影响结果。

性能上,如果对索引列写 DATE(created_at)YEAR(created_at),数据库可能无法直接利用普通索引。更推荐先计算开始和结束时间,用范围条件过滤,再按需要分组展示。

面试中要主动提闭开区间 [start, end),这是处理时间边界最稳的方式。

完整版教学

一、推荐的时间范围写法

SELECT COUNT(*)
FROM orders
WHERE created_at >= TIMESTAMP '2026-07-29 00:00:00'
  AND created_at <  TIMESTAMP '2026-07-30 00:00:00';

左闭右开避免了 23:59:59 漏毫秒、微秒的问题。

也方便连续日期范围拼接,不会重叠。

二、为什么少用 DATE(column) 过滤

WHERE DATE(created_at) = DATE '2026-07-29'

这类写法把函数作用在列上。

普通索引可能无法直接使用。

大表上会变成更多扫描。

如果数据库支持函数索引,也要明确建立对应索引。

三、按天或按月分组

SELECT
  DATE_TRUNC('day', created_at) AS day,
  COUNT(*) AS order_count
FROM orders
WHERE created_at >= TIMESTAMP '2026-07-01 00:00:00'
  AND created_at <  TIMESTAMP '2026-08-01 00:00:00'
GROUP BY DATE_TRUNC('day', created_at)
ORDER BY day;

不同数据库日期截断函数不同。

MySQL、PostgreSQL、Oracle 的写法需要分别调整。

四、关键注意点

问题风险建议
闭区间结束漏掉毫秒或重复统计用左闭右开
函数包列索引利用变差用范围条件过滤
时区不一致日期归属错误明确存储和展示时区
缺失日期折线图断点用日期维表补齐
字段类型混乱比较结果异常区分 date、timestamp、字符串

五、时区问题

数据库中常存 UTC 时间。

用户报表常按本地时区统计。

如果用户在东八区,业务日的 UTC 范围不是 UTC 当天零点到次日零点。

要先把本地业务时间转换成数据库存储时区的查询范围。

日期统计的“今天”必须绑定业务时区,否则跨地区数据很容易错。

六、补齐缺失日期

某天没有订单时,聚合查询不会返回那一天。

报表如果需要连续日期,要用日期维表或生成序列 LEFT JOIN 聚合结果。

这样才能把缺失日期显示为 0。

否则图表会误以为只有有数据的日期存在。

七、误区和追问

  • 误区:一天结束可以写 23:59:59。 字段有毫秒或微秒时可能漏数据。
  • 误区:DATE(created_at) 写起来方便就没问题。 它可能让普通索引失效。
  • 误区:服务器时区就是业务时区。 用户、数据库、服务器和报表口径可能完全不同。
  • 追问:如何统计最近 7 天? 先确定业务时区和边界,再用 [start, end) 范围。
  • 追问:没有数据的日期怎么显示 0? 用日期表或序列生成日期,再 LEFT JOIN 聚合结果。
  • 追问:按月统计要注意什么? 月份长度不同,仍建议用月初到下月初的闭开区间。

八、面试收束

回答日期统计时要同时讲正确性和性能。

闭开区间、索引友好、时区、补齐缺失日期,是比较完整的四个点。