动态分组的时间范围报表SQL查询方案
需求说明
编写SQL查询生成时间范围报表,需根据数据的日期跨度自动切换分组维度:
- 日期跨度小于1个月:按天分组(同一天的记录合并)
- 日期跨度小于1年:按月分组(同一月的记录合并)
- 日期跨度大于1年:按年分组(同一年的记录合并)
同时筛选出指定时间范围内的数据。
通用SQL查询语句
WITH date_range AS (
SELECT
MIN(createdAt) AS start_date,
MAX(createdAt) AS end_date
FROM ViewSell
WHERE branchId = ? -- 替换为目标门店ID
)
SELECT
branchId,
ROUND(SUM(totalPrice), 2) AS sumTotalPrice,
CASE
WHEN DATEDIFF(day, dr.start_date, dr.end_date) < 30 THEN CAST(createdAt AS DATE)
WHEN TIMESTAMPDIFF(year, dr.start_date, dr.end_date) < 1 THEN DATE_FORMAT(createdAt, '%Y-%m-01')
ELSE DATE_FORMAT(createdAt, '%Y-01-01')
END AS timeFrame
FROM ViewSell
CROSS JOIN date_range dr
WHERE branchId = ? -- 与上方替换为相同的门店ID
AND createdAt BETWEEN dr.start_date AND dr.end_date
GROUP BY
branchId,
CASE
WHEN DATEDIFF(day, dr.start_date, dr.end_date) < 30 THEN CAST(createdAt AS DATE)
WHEN TIMESTAMPDIFF(year, dr.start_date, dr.end_date) < 1 THEN DATE_FORMAT(createdAt, '%Y-%m-01')
ELSE DATE_FORMAT(createdAt, '%Y-01-01')
END
ORDER BY timeFrame;
查询逻辑说明
- 计算日期跨度:通过CTE
date_range 获取当前门店数据的最早和最晚日期,确定分组维度的判断依据 - 动态分组:使用
CASE语句根据日期跨度选择分组规则:
- 跨度<30天:将时间戳转为纯日期格式,按天聚合
- 跨度≥30天且<1年:格式化为当月第一天,按月聚合
- 跨度≥1年:格式化为当年第一天,按年聚合
- 数据筛选与聚合:筛选指定门店的时间范围内数据,汇总总价并按时间维度排序
示例验证
示例1:日期跨度小于1个月
原查询
SELECT
*
FROM
ViewSell
WHERE
branchId = 1
ORDER BY
createdAt ASC
原结果
| id | branchId | totalPrice | createdAt |
|---|
| 85 | 1 | 2718.66 | 2022-07-03 08:49:27.727 |
| 26 | 1 | 3832.69 | 2022-07-06 09:08:06.880 |
| 89 | 1 | 9569.85 | 2022-07-07 04:13:09.230 |
| 80 | 1 | 1523.62 | 2022-07-07 04:38:29.313 |
| 15 | 1 | 2500.21 | 2022-07-11 09:01:05.183 |
| 5 | 1 | 6874.03 | 2022-07-14 23:54:05.590 |
| 45 | 1 | 9188.03 | 2022-07-17 05:35:48.560 |
| 98 | 1 | 4426.17 | 2022-07-21 17:35:31.617 |
| 54 | 1 | 3862.86 | 2022-07-22 05:18:28.553 |
| 70 | 1 | 5668.82 | 2022-07-22 06:12:33.867 |
| 65 | 1 | 3653.67 | 2022-07-26 08:29:03.587 |
期望结果
| branchId | sumTotalPrice | timeFrame |
|---|
| 1 | 2718.66 | 2022-07-03 |
| 1 | 3832.69 | 2022-07-06 |
| 1 | 11093.47 | 2022-07-07 |
| 1 | 2500.21 | 2022-07-11 |
| 1 | 6874.03 | 2022-07-14 |
| 1 | 9188.03 | 2022-07-17 |
| 1 | 4426.17 | 2022-07-21 |
| 1 | 9531.68 | 2022-07-22 |
| 1 | 3653.67 | 2022-07-26 |
示例2:日期跨度小于1年
原查询
SELECT
*
FROM
ViewSell
WHERE
branchId = 4
ORDER BY
createdAt ASC
原结果
| id | branchId | totalPrice | createdAt |
|---|
| 52 | 4 | 7502.97 | 2023-11-01 17:49:51.110 |
| 56 | 4 | 7337.75 | 2023-11-06 15:38:57.567 |
| 44 | 4 | 9385.97 | 2024-01-18 11:19:04.460 |
期望结果
| branchId | sumTotalPrice | timeFrame |
|---|
| 4 | 9385.97 | 2024-01-01 |
| 4 | 14840.72 | 2023-11-01 |
示例3:日期跨度大于1年
原查询
SELECT
*
FROM
ViewSell
WHERE
branchId = 2
ORDER BY
createdAt ASC
原结果
| id | branchId | totalPrice | createdAt |
|---|
| 22 | 2 | 5589.39 | 2020-05-23 15:22:14.703 |
| 46 | 2 | 6103.08 | 2020-08-18 03:58:14.973 |
| 48 | 2 | 4905.96 | 2020-10-14 23:57:48.680 |
| 85 | 2 | 8953.03 | 2021-08-15 11:16:34.627 |
| 6 | 2 | 8132.46 | 2021-08-26 21:27:21.627 |
| 53 | 2 | 1913.24 | 2021-09-20 17:41:13.793 |
| 4 | 2 | 3164.81 | 2022-03-18 04:24:40.840 |
| 28 | 2 | 3506.16 | 2022-05-20 17:48:44.330 |
| 37 | 2 | 7256.73 | 2022-07-25 20:45:16.497 |
| 16 | 2 | 7470.38 | 2023-01-22 18:33:07.163 |
| 27 | 2 | 5957.58 | 2023-03-22 03:04:02.687 |
| 99 | 2 | 7722.43 | 2023-04-14 21:22:38.160 |
| 81 | 2 | 4517.39 | 2023-04-25 11:25:17.900 |
| 70 | 2 | 5562.04 | 2023-05-10 08:19:35.200 |
| 55 | 2 | 9343.17 | 2023-11-17 08:56:09.870 |
| 94 | 2 | 1056.38 | 2024-01-03 18:36:24.743 |
| 47 | 2 | 8434.11 | 2024-03-23 09:07:31.743 |
期望结果
| branchId | sumTotalPrice | timeFrame |
|---|
| 2 | 16598.43 | 2020-01-01 |
| 2 | 18998.73 | 2021-01-01 |
| 2 | 13927.70 | 2022-01-01 |
| 2 | 40572.99 | 2023-01-01 |
| 2 | 9490.49 | 2024-01-01 |
内容的提问来源于stack exchange,提问作者Rawand