如何编写 SQL 语句以每两个相邻月的同一天使用 group 对数据求和
How to write a SQL statement to sum data using group by the same day of every two neighboring months
我有这样的数据table:
datetime data
-----------------------
...
2017/8/24 6.0
2017/8/25 5.0
...
2017/9/24 6.0
2017/9/25 6.2
...
2017/10/24 8.1
2017/10/25 8.2
我想写一个SQL语句来在特定时间范围内每两个相邻月份的24日使用组对数据求和,例如:从2017/7/20到2017/10/25如上
这个SQL语句怎么写?我正在使用 SQL Server 2008 R2。
预期的结果table是这样的:
datetime_range data_sum
------------------------------------
...
2017/8/24~2017/9/24 100.9
2017/9/24~2017/10/24 120.2
...
这里进行的一种概念性方法是将 "month" 重新定义为在每个正常月份的 24 日结束。使用 SQL 服务器月份函数,我们会将 24 日之后发生的任何日期指定为属于下一个月。然后我们可以按年份和这个移位的月份进行汇总以获得数据总和。
WITH cte AS (
SELECT
data,
YEAR(datetime) AS year,
CASE WHEN DAY(datetime) > 24
THEN MONTH(datetime) + 1 ELSE MONTH(datetime) END AS month
FROM yourTable
)
SELECT
CONVERT(varchar(4), year) + '/' + CONVERT(varchar(2), month) +
'/25~' +
CONVERT(varchar(4), year) + '/' + CONVERT(varchar(2), (month + 1)) +
'/24' AS datetime_range,
SUM(data) AS data_sum
FROM cte
GROUP BY
year, month;
请注意,您建议的范围似乎包括两端的第 24 位,从会计的角度来看这没有意义。我假设这个月包括并结束于 24 日(即 25 日是下一个会计期间的第一天。
我认为最简单的方法是减去 25 天并按月汇总:
select year(dateadd(day, -25, datetime)) as yr,
month(dateadd(day, -25, datetime)) as mon,
sum(data)
from t
group by dateadd(day, -25, datetime);
您可以格式化 yr
和 mon
以获取特定范围的日期,但这会进行聚合(并且 yr
/mon
列可能是够了)。
第 0 步:构建日历 table。每个数据库最终都需要一个日历 table 来简化这种计算。
在此 table 中,您可能有以下列:
- 日期(主键)
- 天
- 月份
- 年
- 季度
- 半年(例如 1 或 2)
- 一年中的第几天(1 到 366)
- 星期几(数字或文本)
- 是周末(现在看起来多余,但以后可以节省大量时间)
- 会计年度 quarter/year(如果贵公司的会计年度不是从 1 月 1 日开始)
- 是假期
- 等等
如果您的公司在 24 日开始其月份,那么您可以添加代表该日期的 "Fiscal Month" 列。
第 1 步:加入日历 table
第 2 步:按日历中的列分组 table。
日历 table 起初听起来很奇怪,但一旦你意识到它们实际上很小,即使它们跨越几百年,它们很快就会成为一项重要资产。
不要试图通过使用计算列来节省磁盘 space。您需要真正的列,因为它们更快并且可以在必要时建立索引。 (老实说,通常仅 PK 索引就足以满足更宽的日历 tables。)
我建议动态构建一些日期范围行,这样您就可以将数据加入到那些行中进行聚合,就像这个例子:
+----+---------------------+---------------------+----------------+
| | period_start_dt | period_end_dt | your_data_here |
+----+---------------------+---------------------+----------------+
| 1 | 24.04.2017 00:00:00 | 24.05.2017 00:00:00 | 1 |
| 2 | 24.05.2017 00:00:00 | 24.06.2017 00:00:00 | 1 |
| 3 | 24.06.2017 00:00:00 | 24.07.2017 00:00:00 | 1 |
| 4 | 24.07.2017 00:00:00 | 24.08.2017 00:00:00 | 1 |
| 5 | 24.08.2017 00:00:00 | 24.09.2017 00:00:00 | 1 |
| 6 | 24.09.2017 00:00:00 | 24.10.2017 00:00:00 | 1 |
| 7 | 24.10.2017 00:00:00 | 24.11.2017 00:00:00 | 1 |
| 8 | 24.11.2017 00:00:00 | 24.12.2017 00:00:00 | 1 |
| 9 | 24.12.2017 00:00:00 | 24.01.2018 00:00:00 | 1 |
| 10 | 24.01.2018 00:00:00 | 24.02.2018 00:00:00 | 1 |
| 11 | 24.02.2018 00:00:00 | 24.03.2018 00:00:00 | 1 |
| 12 | 24.03.2018 00:00:00 | 24.04.2018 00:00:00 | 1 |
+----+---------------------+---------------------+----------------+
declare @start_dt date;
set @start_dt = '20170424';
select
period_start_dt, period_end_dt, sum(1) as your_data_here
from (
select
dateadd(month,m.n,start_dt) period_start_dt
, dateadd(month,m.n+1,start_dt) period_end_dt
from (
select @start_dt start_dt ) seed
cross join (
select 0 n union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9 union all
select 10 union all
select 11
) m
) r
-- LEFT JOIN YOUR DATA
-- ON yourdata.date >= r.period_start_dt and data.date < r.period_end_dt
group by
period_start_dt, period_end_dt
在加入您的数据时,请不要试图使用 "between"。请按照上面的说明使用 yourdata.date >= r.period_start_dt and data.date < r.period_end_dt
否则您可能会重复计算信息,因为 between 包括下限和上限。
我有这样的数据table:
datetime data
-----------------------
...
2017/8/24 6.0
2017/8/25 5.0
...
2017/9/24 6.0
2017/9/25 6.2
...
2017/10/24 8.1
2017/10/25 8.2
我想写一个SQL语句来在特定时间范围内每两个相邻月份的24日使用组对数据求和,例如:从2017/7/20到2017/10/25如上
这个SQL语句怎么写?我正在使用 SQL Server 2008 R2。
预期的结果table是这样的:
datetime_range data_sum
------------------------------------
...
2017/8/24~2017/9/24 100.9
2017/9/24~2017/10/24 120.2
...
这里进行的一种概念性方法是将 "month" 重新定义为在每个正常月份的 24 日结束。使用 SQL 服务器月份函数,我们会将 24 日之后发生的任何日期指定为属于下一个月。然后我们可以按年份和这个移位的月份进行汇总以获得数据总和。
WITH cte AS (
SELECT
data,
YEAR(datetime) AS year,
CASE WHEN DAY(datetime) > 24
THEN MONTH(datetime) + 1 ELSE MONTH(datetime) END AS month
FROM yourTable
)
SELECT
CONVERT(varchar(4), year) + '/' + CONVERT(varchar(2), month) +
'/25~' +
CONVERT(varchar(4), year) + '/' + CONVERT(varchar(2), (month + 1)) +
'/24' AS datetime_range,
SUM(data) AS data_sum
FROM cte
GROUP BY
year, month;
请注意,您建议的范围似乎包括两端的第 24 位,从会计的角度来看这没有意义。我假设这个月包括并结束于 24 日(即 25 日是下一个会计期间的第一天。
我认为最简单的方法是减去 25 天并按月汇总:
select year(dateadd(day, -25, datetime)) as yr,
month(dateadd(day, -25, datetime)) as mon,
sum(data)
from t
group by dateadd(day, -25, datetime);
您可以格式化 yr
和 mon
以获取特定范围的日期,但这会进行聚合(并且 yr
/mon
列可能是够了)。
第 0 步:构建日历 table。每个数据库最终都需要一个日历 table 来简化这种计算。
在此 table 中,您可能有以下列:
- 日期(主键)
- 天
- 月份
- 年
- 季度
- 半年(例如 1 或 2)
- 一年中的第几天(1 到 366)
- 星期几(数字或文本)
- 是周末(现在看起来多余,但以后可以节省大量时间)
- 会计年度 quarter/year(如果贵公司的会计年度不是从 1 月 1 日开始)
- 是假期
- 等等
如果您的公司在 24 日开始其月份,那么您可以添加代表该日期的 "Fiscal Month" 列。
第 1 步:加入日历 table
第 2 步:按日历中的列分组 table。
日历 table 起初听起来很奇怪,但一旦你意识到它们实际上很小,即使它们跨越几百年,它们很快就会成为一项重要资产。
不要试图通过使用计算列来节省磁盘 space。您需要真正的列,因为它们更快并且可以在必要时建立索引。 (老实说,通常仅 PK 索引就足以满足更宽的日历 tables。)
我建议动态构建一些日期范围行,这样您就可以将数据加入到那些行中进行聚合,就像这个例子:
+----+---------------------+---------------------+----------------+
| | period_start_dt | period_end_dt | your_data_here |
+----+---------------------+---------------------+----------------+
| 1 | 24.04.2017 00:00:00 | 24.05.2017 00:00:00 | 1 |
| 2 | 24.05.2017 00:00:00 | 24.06.2017 00:00:00 | 1 |
| 3 | 24.06.2017 00:00:00 | 24.07.2017 00:00:00 | 1 |
| 4 | 24.07.2017 00:00:00 | 24.08.2017 00:00:00 | 1 |
| 5 | 24.08.2017 00:00:00 | 24.09.2017 00:00:00 | 1 |
| 6 | 24.09.2017 00:00:00 | 24.10.2017 00:00:00 | 1 |
| 7 | 24.10.2017 00:00:00 | 24.11.2017 00:00:00 | 1 |
| 8 | 24.11.2017 00:00:00 | 24.12.2017 00:00:00 | 1 |
| 9 | 24.12.2017 00:00:00 | 24.01.2018 00:00:00 | 1 |
| 10 | 24.01.2018 00:00:00 | 24.02.2018 00:00:00 | 1 |
| 11 | 24.02.2018 00:00:00 | 24.03.2018 00:00:00 | 1 |
| 12 | 24.03.2018 00:00:00 | 24.04.2018 00:00:00 | 1 |
+----+---------------------+---------------------+----------------+
declare @start_dt date;
set @start_dt = '20170424';
select
period_start_dt, period_end_dt, sum(1) as your_data_here
from (
select
dateadd(month,m.n,start_dt) period_start_dt
, dateadd(month,m.n+1,start_dt) period_end_dt
from (
select @start_dt start_dt ) seed
cross join (
select 0 n union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9 union all
select 10 union all
select 11
) m
) r
-- LEFT JOIN YOUR DATA
-- ON yourdata.date >= r.period_start_dt and data.date < r.period_end_dt
group by
period_start_dt, period_end_dt
在加入您的数据时,请不要试图使用 "between"。请按照上面的说明使用 yourdata.date >= r.period_start_dt and data.date < r.period_end_dt
否则您可能会重复计算信息,因为 between 包括下限和上限。