如何编写一个 SQL 语句以每两个相邻月的同一天使用 group 对数据求和
Posted
技术标签:
【中文标题】如何编写一个 SQL 语句以每两个相邻月的同一天使用 group 对数据求和【英文标题】:How to write a SQL statement to sum data using group by the same day of every two neighboring months 【发布时间】:2017-11-12 03:22:08 【问题描述】:我有一个这样的数据表:
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 日使用 group 对数据求和,例如:从 2017/7/20 到 2017/10/25 如上所述。
这个SQL语句怎么写?我正在使用 SQL Server 2008 R2。
预期结果表是这样的:
datetime_range data_sum
------------------------------------
...
2017/8/24~2017/9/24 100.9
2017/9/24~2017/10/24 120.2
...
【问题讨论】:
原谅?我不明白。 想知道到目前为止您为生成所需结果所做的尝试。如果有的话,你可以在问题中发布代码吗? 我想写一个sql来得到如下的预期结果表 【参考方案1】:在这里进行的一种概念方法是将“月份”重新定义为在每个正常月份的 24 日结束。使用 SQL Server 月份函数,我们会将 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 日是下一个会计期间的第一天。
Demo
【讨论】:
【参考方案2】:我建议动态构建一些日期范围行,这样您就可以将数据连接到那些用于聚合的行,例如:
+----+---------------------+---------------------+----------------+
| | 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 |
+----+---------------------+---------------------+----------------+
DEMO
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
在加入您的数据时,请不要试图使用“介于”。按照上面的说明并使用yourdata.date >= r.period_start_dt and data.date < r.period_end_dt
,否则您可能会重复计算信息,因为介于之间的信息包括下限和上限。
【讨论】:
【参考方案3】:我认为最简单的方法是减去 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
列可能就足够了)。
【讨论】:
【参考方案4】:第 0 步:构建日历表。每个数据库最终都需要一个日历表来简化这种计算。
在此表中,您可能有如下列:
日期(主键) 天 月 年份 季度 半年(例如 1 年或 2 年) 一年中的某天(1 到 366) 星期几(数字或文本) 是周末(现在似乎多余,但以后可以节省大量时间) 财政季度/年度(如果贵公司的财政年度不是从 1 月 1 日开始) 放假了 等如果您的公司从 24 日开始其月份,那么您可以添加一个“财政月份”列来代表它。
第 1 步:加入日历表
第 2 步:按日历表中的列分组。
日历表起初听起来很奇怪,但一旦你意识到它们实际上很小,即使它们跨越了几百年,它们很快就会成为一项重要资产。
不要试图通过使用计算列来节省磁盘空间。您需要真正的列,因为它们要快得多,并且可以在必要时进行索引。 (虽然老实说,通常只有 PK 索引就足以用于更宽的日历表。)
【讨论】:
是的,你是对的,我确实像你说的那样做了。但我认为这些列是多余的,因为我可以随时将这些列添加到表中并拆分日期时间列在我真正需要时获取这些列的数据。 是的,但是运行这些功能与仅查找数据相比有多昂贵?只有测试才能说明问题,但 SQL Server 中的标量函数出奇地糟糕,经常会搞砸诸如并行运行查询之类的事情。以上是关于如何编写一个 SQL 语句以每两个相邻月的同一天使用 group 对数据求和的主要内容,如果未能解决你的问题,请参考以下文章